Я свернул свой собственный, но есть immer
библиотека в качестве довольно всеобъемлющего примера, и она специально вдохновлена clojure.Я был взволнован и откатился несколько лет назад после прослушивания речи Джона Кармака, где он прыгал по всем направлениям функционального программирования.Казалось, он мог представить игровой движок, вращающийся вокруг неизменных структур данных.Хотя он не вдавался в подробности, и хотя в его голове это выглядело как смутная идея, тот факт, что он всерьез обдумывал это и, похоже, не думал, что накладные расходы резко снизят частоту кадров, был достаточным, чтобы меня взволноватьоб исследовании этой идеи.
Я на самом деле использую ее в качестве детали оптимизации, которая может показаться парадоксальной (накладные расходы на неизменность), но я имею в виду в конкретном контексте.Если я абсолютно хочу сделать это:
// We only need to change a small part of this huge data structure.
HugeDataStructure transform(HugeDataStructure input);
... и я абсолютно не хочу, чтобы функция вызывала побочные эффекты, чтобы она могла быть поточно-ориентированной и никогда не использовалась неправильно, тогда у меня естьнет другого выбора, кроме как скопировать огромную структуру данных (которая может занимать гигабайт).
И там я обнаружил, что небольшая библиотека неизменяемых структур данных чрезвычайно полезна в таком контексте, поскольку это делает описанный выше сценарий относительно дешевымпросто поверхностное копирование и ссылки на неизмененные части.Тем не менее, я в основном просто использую одну неизменяемую структуру данных, которая в основном представляет собой последовательность произвольного доступа, например:
И, как уже упоминалось, этоЯ действительно нуждался в некотором уходе, настройке и всестороннем тестировании, а также во многих сеансах 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();
}
У меня даже есть неизменяемая библиотека изображений, которую я использую для редактирования изображений, чтобы упростить неразрушающее редактирование.Он использует стратегию, аналогичную приведенной выше структуре, обрабатывая изображения как плитки следующим образом:
Когда переходный процесс изменяется, и мы получаем новый неизменяемый,только измененные детали сделаны уникальными.Остальные фрагменты копируются мелко (только 32-битные индексы):
Я использую их в областях, довольно критичных к производительности, таких как сетка иобработка видео.Была проведена небольшая настройка объема данных, который должен хранить каждый блок (слишком много, и мы тратим впустую обработку и глубокое копирование памяти, слишком много, слишком мало, и мы тратим впустую обработку и поверхностное копирование слишком большого количества указателей с более частыми блокировками потоков).
Я не использую их для трассировки лучей, так как это одна из самых критических областей производительности, которую можно себе представить, и малейшая часть накладных расходов заметна для пользователей (они фактически измеряют и замечают различия в производительности в диапазоне2%), но в большинстве случаев они достаточно эффективны, и это довольно удивительное преимущество, когда вы можете копировать эти огромные структуры данных в целом влево и вправо для упрощения безопасности потоков, отмены систем, неразрушающего редактирования,и т.д., не беспокоясь о взрывном использовании памяти и заметных задержках, затрачиваемых на глубокое копирование всего.