живые объекты С++, которые живут в файлах с отображением памяти?

Итак, я прочитал это интервью с Джоном Кармаком в Gamasutra, в котором он говорит о том, что он называет «живыми объектами C++, которые живут в файлах с отображением памяти». Вот несколько цитат:

ДЧ: Да. И я на самом деле получаю от этого много преимуществ в этом... Последний проект iOS Rage, мы поставляли с некоторой новой технологией, которая использует некоторые умные вещи для создания живых объектов C++, которые живут в отображенных в память файлах, поддерживаемых файловой системой флэш-памяти на Вот как я хочу структурировать всю нашу будущую работу на ПК.

...

Мой приказ самому себе: я хочу, чтобы игра загружалась в течение двух секунд на нашей платформе ПК, чтобы мы могли выполнять итерации намного быстрее. И прямо сейчас, даже с твердотельными накопителями, над вами доминируют все, что вы делаете во время загрузки, поэтому требуется эта другая дисциплина, чтобы иметь возможность сказать: «Все будет уничтожено и использовано в относительных адресах». поэтому вы просто говорите: «Сопоставьте файл, все мои ресурсы тут же, и это делается за 15 миллисекунд».

(Полное интервью можно найти здесь)

Есть ли у кого-нибудь идеи, о чем говорит Кармак и как бы вы устроили что-то подобное? Я немного искал в Интернете, но я не могу найти ничего по этому поводу.


person Mart    schedule 23.08.2011    source источник
comment
Я думаю, что он десериализует неизменяемые объекты С++ из флэш-памяти. Это всегда немного сложно/рискованно, потому что обычно вы не можете контролировать выделение памяти/ресурсов объектами, если вы не написали код для объектов.   -  person xanatos    schedule 23.08.2011
comment
Вы занимаетесь мобильной разработкой? Это звучит полезно в основном, когда вам нужно иметь возможность быстро переключаться в приложение и выходить из него, что на самом деле не является проблемой на обычном компьютере.   -  person Kerrek SB    schedule 23.08.2011
comment
@Kerrek полезен для игр на любой платформе или всего, что требует загрузки большого количества состояния с диска.   -  person Justicle    schedule 23.08.2011
comment
@Justicle: Это, без сомнения, хорошо для сериализации, но не будет ли это относительно медленным на рабочем столе по сравнению с использованием реальной системной памяти?   -  person Kerrek SB    schedule 23.08.2011
comment
В том то и дело, чтобы ускорить загрузку с диска В системную память.   -  person Justicle    schedule 26.08.2011


Ответы (5)


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

person sharptooth    schedule 23.08.2011
comment
Недостатком является то, что в случае сбоя вы можете получить поврежденные данные. - person R. Martinho Fernandes; 23.08.2011
comment
В качестве альтернативы вы можете использовать указатели, но также хранить базу карт. Затем вы можете переустанавливать свои указатели при загрузке. - person Don Reba; 23.08.2011
comment
Чтобы получить некоторое представление, см. Microsoft __ключевое слово на основе. - person MSalters; 23.08.2011
comment
Или вы можете использовать Memento и сериализовать сувенир вместо ваших реальных объектов;). - person AlexTheo; 22.11.2012

Вы бы использовали новое размещение либо напрямую, либо через настраиваемые распределители.

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

Бесплатное подмножество EASTL находится здесь:

person sehe    schedule 23.08.2011

Мы используем в течение многих лет то, что мы называем «относительными указателями», что является своего рода умным указателем. Он по своей сути нестандартен, но хорошо работает на большинстве платформ. Он устроен так:

template<class T>
class rptr
{
    size_t offset;
public:
    T* operator->() { return reinterpret_cast<T*>(reinterpret_cast<char*>(this)+offset); }
};

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

Чтобы всегда иметь непротиворечивые данные, мы используем моментальные снимки с помощью трюков COW mmap (которые работают в пользовательском пространстве на Linux, не знаю о других ОС).

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

person PlasmaHH    schedule 23.08.2011
comment
Существует опасность использования фиксированных адресов для сопоставления вашего файла — ОС может позже изменить правила и использовать этот диапазон адресов для каких-либо других целей. Например, он может загрузить программный код в диапазон адресов, который вы зарезервировали для данных. - person Skizz; 23.08.2011
comment
Действительно, именно поэтому мы используем зарезервированные области, которые ОС не планирует использовать ни для чего. - person PlasmaHH; 23.08.2011
comment
@PlasmaHH Как вы находите такие зарезервированные зоны? Где они задокументированы? Они разные на каждой ОС? - person fadedbee; 12.09.2013
comment
@chrisdew: они разные для каждой ОС и конфигурации ОС. В Linux есть возможность проверить, какие из ваших материалов и где отображаются через /proc, и поэтому вы можете найти интересующие вас области, в которые ОС никогда ничего не помещает. Если вы хотите убедиться в этом, вы читаете исходный код ОС. - person PlasmaHH; 30.09.2013
comment
Как работает reinterpret_cast<char*>(this)? Почему бы не использовать переменную global_mmap_offset? - person noɥʇʎԀʎzɐɹƆ; 25.01.2020
comment
@ noɥʇʎԀʎzɐɹƆ: Он просто берет адрес таким образом, чтобы на нем можно было манипулировать смещением байтов. Глобальная переменная смещения будет работать, если у вас когда-либо есть только одна вещь, которую вы ммапируете, но вы можете сделать это для двух или более файлов, которые затем получат относительные несовместимые смещения. - person PlasmaHH; 21.02.2020

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

class IFile
{
public:
  IFile (class FileSystem &owner);
  virtual Seek (...);
  virtual Read (...);
  virtual GetFilePosition ();
};

и дополнительный класс:

class FileSystem
{
public:
  BeginStreaming (filename);
  EndStreaming ();
  IFile *CreateFile ();
};

и вы бы написали код загрузки, например:

void LoadLevel (levelname)
{
  FileSystem fs;
  fs.BeginStreaming (levelname);
  IFile *file = fs.CreateFile (level_map_name);
  ReadLevelMap (fs, file);
  delete file;
  fs.EndStreaming ();
}

void ReadLevelMap (FileSystem &fs, IFile *file)
{
  read some data from fs
  get names of other files to load (like textures, object definitions, etc...)
  for each texture file
  {
    IFile *texture_file = fs.CreateFile (some other file name)
    CreateTexture (texture_file);
    delete texture_file;
  }
}

Тогда у вас будет три режима работы: режим отладки, режим сборки потокового файла и режим выпуска.

В каждом режиме объект FileSystem будет создавать разные объекты IFile.

В режиме отладки объект IFile просто обертывает стандартные функции ввода-вывода.

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

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

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

Побочным эффектом является то, что данные удаляются из ненужных метаданных, которые обычно пропускаются.

У него есть недостатки - все данные для уровня находятся в одном файле. Они могут быть довольно большими, и данные не могут быть разделены между файлами, если у вас есть набор текстур, скажем, общих для двух или более уровней, данные будут дублироваться в каждом файле потока. Кроме того, процесс загрузки должен быть одинаковым каждый раз при загрузке данных, вы не можете условно пропускать или добавлять элементы на уровень.

person Skizz    schedule 23.08.2011
comment
@Justicle: Я не согласен: я хочу, чтобы игра загружалась за две секунды на нашей платформе ПК, и это один из способов очень быстрой загрузки игровых данных. - person Skizz; 26.08.2011
comment
Вопрос касается сериализации объектов в файлах с отображением памяти с использованием указателей смещения, ваш пример касается объединения файлов (другой метод). Цитата - это не вопрос, это отсылка. - person Justicle; 27.08.2011
comment
Т.е. ваш код классный, но он не отвечает на заданный вопрос. - person Justicle; 27.08.2011

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

Вместо этого вы делаете один fread (или эквивалент), например, файл уровня в память, а затем просто исправляете указатели.

person Andreas Brinck    schedule 23.08.2011