Строковые идентификаторы в движке. string pools.
Автор: samrrr
В программировании игр часто приходится ссылаться на объекты, используя строковые идентификаторы: например, получить текстуру по имени её файла или найти актора по его имени. Однако, хранение и частое сравнение большого количества строк может оказаться дорогим как с вычислительной точки зрения, так и с точки зрения используемой памяти.
Введение
Обзор способов представления строки
Способ представления: string
Способ представления: string + static unordered_map
Способ представления: указатель на строку в глобальном хранилище строк
Способ представления: индекс типизированный
Исходники
Дополнение: имена объектов
Дополнение: типизация имён по типу ресурсов.
Введение
В этой статье рассмотрены различные способы представления строки, которые позволяют оптимизировать сравнение строк и размер одного инстанса строки.
Обзор способов представления строки
Рассматриваемые основные способы:
Способ | Плюсы | Минусы |
---|---|---|
string | -просто | -медленно
-много памяти занимает |
string+static unordered_map | -просто
-быстрое сравнение | -строки далеко друг от друга в памяти. |
указатель на строку в глобальном хранилище строк | -быстрое сравнение
-эффективное использование памяти | -сложно |
индекс типизированный | -быстрое сравнение
-самое эффективное использование памяти sizeof(Name)-2(65536) или 3(16777216) | -в отладчике имя ресурса невидно
-вывести для отладки сложно |
Способы через равенство "Str" == "Str", хеши, кодоген енама строк, COW строки рассмотрены не будут, так как ведут к множеству неочевидных проблем.
А теперь подробнее про эти методы.
Далее при представлении используются следующие обозначения:
D-длина строки
Памяти: байт на инстанс.
Время сравнения: временная сложность O(...).
Способ представления: string
Просто строка.
Для больших проектов, где имена ресурсов могут быть 500+ символов этот способ не подходит из-за его низкой скорости сравнения строк и большого использования памяти.
Можно, конечно, как в старой файловой сисеме FAT сказать, что не делаем длинных путей и сокращаем гласные в именах, но это неудобно, особенно в больших проектах.
Памяти: 16(или больше) + D(если строка не влезла в хранилище самого класса).
Время сравнения: D.
class NameString { public: explicit NameString(const char* name): _s( name) {} string get( ) { return _s; } std::strong_ordering operator<=>( NameString const& b) const = default; private: string _s; };
Способ представления: string + static unordered_map
Первый же запрос из гугла выдаёт способ с помошью статической переменной и свойств emplace.
https://stackoverflow.com/questions/20938441/implementing-a-strin… d-not-to-move
Статическая переменная выступает в роли синглтона. Создаёт инстанс и даёт к нему доступ.
А emplace возвращает итератор на уже имеющийся элемент или создаёт новый.
Сам unordered_set не инвалидирует указатели при добавлении элементов.
В итоге пара строк и простейший пуул готов.
Эффективность такого пуула очень низка. Строки находятся очень далеко в памяти и от этого запрос к строке практически гарантированно кэш мисс, скорее всего по всем 3 уровням. В итоге очень долгий запрос к памяти.
Зато просто реализовать.
Памяти: 8.
Время сравнения: 1.