"живые объекты C ++, которые живут в файлах с отображением в памяти"? - PullRequest
14 голосов
/ 23 августа 2011

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

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

...

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

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

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

Ответы [ 5 ]

7 голосов
/ 23 августа 2011

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

1 голос
/ 23 августа 2011

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

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 также обернул стандартный IO, но имел дополнительные функции записи в потоковый файл (владелец FileSystem открыл потоковый файл) каждый прочитанный байт и записи возвращаемого значения любого файла. запросы позиции указателя (поэтому, если нужно знать размер файла, эта информация записывается в файл потока). Это могло бы объединить различные файлы в один большой файл, но только данные, которые были фактически прочитаны.

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

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

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

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

1 голос
/ 23 августа 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 бит адресного пространства, мы выбрали зарезервированную область памяти для наших приложений, с которой мы всегда сопоставляем такой файл.

1 голос
/ 23 августа 2011

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

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

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

0 голосов
/ 23 августа 2011

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

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

...