Постоянные структуры данных в c ++ - PullRequest
17 голосов
/ 09 декабря 2010

Существуют ли какие-либо постоянные реализации структур данных в c ++, подобные тем, что в clojure?

Ответы [ 3 ]

5 голосов
/ 25 декабря 2017

Я свернул свой собственный, но есть immer библиотека в качестве довольно всеобъемлющего примера, и она специально вдохновлена ​​clojure.Я был взволнован и откатился несколько лет назад после прослушивания речи Джона Кармака, где он прыгал по всем направлениям функционального программирования.Казалось, он мог представить игровой движок, вращающийся вокруг неизменных структур данных.Хотя он не вдавался в подробности, и хотя в его голове это выглядело как смутная идея, тот факт, что он всерьез обдумывал это и, похоже, не думал, что накладные расходы резко снизят частоту кадров, был достаточным, чтобы меня взволноватьоб исследовании этой идеи.

Я на самом деле использую ее в качестве детали оптимизации, которая может показаться парадоксальной (накладные расходы на неизменность), но я имею в виду в конкретном контексте.Если я абсолютно хочу сделать это:

// We only need to change a small part of this huge data structure.
HugeDataStructure transform(HugeDataStructure input);

... и я абсолютно не хочу, чтобы функция вызывала побочные эффекты, чтобы она могла быть поточно-ориентированной и никогда не использовалась неправильно, тогда у меня естьнет другого выбора, кроме как скопировать огромную структуру данных (которая может занимать гигабайт).

И там я обнаружил, что небольшая библиотека неизменяемых структур данных чрезвычайно полезна в таком контексте, поскольку это делает описанный выше сценарий относительно дешевымпросто поверхностное копирование и ссылки на неизмененные части.Тем не менее, я в основном просто использую одну неизменяемую структуру данных, которая в основном представляет собой последовательность произвольного доступа, например:

enter image description here

И, как уже упоминалось, этоЯ действительно нуждался в некотором уходе, настройке и всестороннем тестировании, а также во многих сеансах VTune, чтобы сделать его поточно-ориентированным и эффективным, но после того, как я применил консистентную смазку, это определенно упростило задачу.

Наверхавтоматической безопасности потоков, когда мы используем эти структуры для написания функций, свободных от побочных эффектов, вы также получаете такие вещи, как неразрушающее редактирование, тривиальные системы отмены, тривиальная исключительная безопасность (нет необходимости откатывать побочные эффекты с помощью средств защиты границ вфункция, которая не вызывает ничего в исключительных путях), и позволяет пользователям копировать и вставлять данные и копировать их, не занимая много памяти, до тех пор, пока они не изменят то, что они вставили в качестве бонуса.На самом деле я обнаружил, что эти бонусы на ежедневной основе даже более полезны, чем безопасность потоков.

Я использую «переходные процессы» (или «строители») как способ выражения изменений в структуре данных, например:

Immutable transform(Immutable input)
{
    Transient transient(input);

    // make changes to mutable transient.
    ...

    // Commit the changes to get a new immutable
    // (this does not touch the input).
    return transient.commit();
}

У меня даже есть неизменяемая библиотека изображений, которую я использую для редактирования изображений, чтобы упростить неразрушающее редактирование.Он использует стратегию, аналогичную приведенной выше структуре, обрабатывая изображения как плитки следующим образом:

enter image description here

Когда переходный процесс изменяется, и мы получаем новый неизменяемый,только измененные детали сделаны уникальными.Остальные фрагменты копируются мелко (только 32-битные индексы):

enter image description here

Я использую их в областях, довольно критичных к производительности, таких как сетка иобработка видео.Была проведена небольшая настройка объема данных, который должен хранить каждый блок (слишком много, и мы тратим впустую обработку и глубокое копирование памяти, слишком много, слишком мало, и мы тратим впустую обработку и поверхностное копирование слишком большого количества указателей с более частыми блокировками потоков).

Я не использую их для трассировки лучей, так как это одна из самых критических областей производительности, которую можно себе представить, и малейшая часть накладных расходов заметна для пользователей (они фактически измеряют и замечают различия в производительности в диапазоне2%), но в большинстве случаев они достаточно эффективны, и это довольно удивительное преимущество, когда вы можете копировать эти огромные структуры данных в целом влево и вправо для упрощения безопасности потоков, отмены систем, неразрушающего редактирования,и т.д., не беспокоясь о взрывном использовании памяти и заметных задержках, затрачиваемых на глубокое копирование всего.

2 голосов
/ 09 декабря 2010

Основная сложность получить постоянную структуру данных - это отсутствие сборки мусора.

Если у вас нет правильной схемы сбора мусора, то вы можете получить плохую (а именно подсчет ссылок), но это означает, что вам нужно быть очень осторожным, НЕ создавать циклические ссылки.

Это меняет само ядро ​​структуры.Например, подумайте о бинарном дереве.Если вы создаете новую версию узла, то вам нужна новая версия его родителя для доступа к нему (и т. Д.).Теперь, если отношение двустороннее (child <-> parent), вы фактически продублируете всю структуру.Это означает, что у вас будет либо родительское -> дочернее отношение, либо противоположное (менее распространенное).

Я могу подумать о реализации двоичного дерева или B-дерева.Например, я не понимаю, как получить правильный массив.

С другой стороны, я согласен, что было бы здорово иметь эффективные массивы в многопоточных средах.

0 голосов
/ 09 декабря 2010

Если я правильно понимаю вопрос, то вам нужна способность дублировать объект, фактически не оплачивая дублирование, когда оно сделано, только тогда, когда это необходимо.Изменения в любом объекте могут быть сделаны без повреждения другого.Это известно как «копирование при записи».

Если это то, что вы ищете, это может быть легко реализовано в C ++ с использованием разделяемых указателей (см. Shared_ptr из Boost, как одна реализация).Первоначально копия будет делиться всем с источником, но после внесения изменений соответствующие части общих указателей объекта заменяются другими общими указателями, указывающими на вновь созданные глубоко копируемые объекты.(Я понимаю, что это объяснение расплывчато - если это действительно то, что вы имеете в виду, ответ может быть разработан).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...