Про строки

В процессе портирования одной игры на Sony PSP возникла “классическая” проблема с нехваткой памяти. Впихнуть в N/10 мегабайт памяти игрушку – ещё то развлечение.

Игра была написана, можно сказать по книжке про C++ и STL, но, к счастью, без книжки Александреску. Код довольно чистый и аккуратный, но, поскольку товарищи считают, что памяти много, то аллокации считать не надо, и можно смело делать, например, new ButtonPressedEvent() внутри кадра (это обеспечило дикое, по моему мнению, количество выделений памяти).

Здесь я расскажу ещё и о том, что в проекте всюду использовался std::string (и wstring) (на самом деле был самодельный клон, который по своей сути работал точно также, но позволял делать гадкую вещь – присваивать юникодным контейнерам неюникодные строки и наоборот, в остальном – тоже самое).

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

В общем в результате при входе в главное меню у нас наблюдалась картина:

nAllocatedBlocks 47089
nAllocatedSize 14563733
peakAllocationSize 17645007

Как мы видим – 47 тыс аллокаций, дающих в результате 14,5 МБ.

Решено было перевести такую безобидную вещь, как Filename (т.е. имя файла с ресурсами, которые загружаются и по которым потом работают ещё всякие мапы и прочие поиски – тоже зло, конечно) с std::wstring (а иногда std::string) на фиксированую строку rk::String<1024> (в зависимости от настроек компилятора она внутри или char, или wchar_t, в нашем случае wchar_t).

Возни при переделке было очень много. Из злобных вещей, которые сразу всплывали – были “неявные” конверсии между ANSI – Unicode – т.е. выражение вида std::wstring filename = “texture.png” на самом деле вызывало бурю эмоций у системы, т.к. нужно было сначала создать строку ansi, создать буфер в памяти, сконвертить её в юникод и потом записать в целевой контейнер. И это только потому что автор забыл написать перед строчкой букву L (L”text”). Ну а поскольку встречались эпические куски кода, где разные строки конкатенировались  из фрагментов, где часть была анси, а часть юникод, чтобы получить полный путь к ресурсу – то получаемый оверхед представляется значительным.

После того, как весь код, связанный с именами файлов был переведён на фиксированые строки и использование только юникодных строк у нас получилась следующая картинка в главном меню:

nAllocatedBlocks 16978
nAllocatedSize 10259790
peakAllocationSize 12734865

Как мы видим – количество произведённых аллокаций уменьшилось почти в 3 раза, это в основном заслуга того, что исчезли неявные конверсии кодировок.

А вот выигрыш по памяти – почти 5МБ (30%) – это уже серьёзная заявка на успех, при том, что rk::String<1024> явный оверкилл и перерасход для имён файлов, длина которых врядле превысит 128 символов (хотя скорее всего здесь была или жуткая ошибка в логике игры, и строки плодились, как кролики, или странная странность в измерениях, но это поведение я буду и дальше изучать, вместе с дальнейшим выпиливанием динамических строк).

Собсно что я хотел рассказать в этом посте:

Старайтесь использовать фиксированые строки. Как правило, в игровом коде нету необходимости динамически менять строки и делать с ними всякие гадкие вещи. Если же вы видите, что ну никак без этого не обойтись – то это скорее всего из-за того, что, либо у вас очень высокая квалификация и вы точно знаете, что делаете и как оно будет работать, либо у вас наоборот – не очень высокая квалификация – и тогда стоит подумать ещё над проектом – может всё-таки найдётся возможность обойтись?

Не смешивайте в рамках одного проекта разные кодировки, особенно с неявными преобразованиями (их вообще лучше запретить под страхом луча поноса в сторону автора). Изначально определитесь, ANSI, UTF-8, UTF-16 или ещё что-то и пользуйтесь чем-то одним. Идеально – все статические строки оборачивать макросом типа _T(), который на основании настроек компиляции будет выбирать кодировку. Этим вы облегчите жизнь не только себе, когда внезапно окажется, что нужно делать японскую локализацию, но и тем людям, которые в будущем будут поддерживать ваш код. Ну и избежите ненужного оверхеда 🙂

Similar Posts

  • История родного города

    Бродил я по просторам интернетов и наткнулся на эту прекрасную ссылку: Киев Сквозь Время А там, карта Киева 1914 года и куча фотографий того времени, стоящих рядом с современными – для сравнения, и краткая историческая справка описывающая место, при желании можно почитать и расширенную. Очень удивительно и познавательно. Кто бы мог подумать, что в центре…

  • Похоже сессия

    Похоже, что пришла ОНА! Сегодня, когда я узнал, что я не допущен к половине зачетов, то осознал, куда я попал и расслабился я рано, поэтому пришлось напрячься и целых 2 часа с Серегой учить вторую половину зачетов, которую после этого мы сдали слегка 🙂 Это были “Основы банковской деятельности”, за два часа я постиг великие…

  • Про автобаны

    Пока память ещё немного свежа – выдам фактические факты по автобанам, которые нам довелось испробовать. Итак: Венгрия – нужно до въезда найти заправку и купить там “матрицу-виньетку”, минимум 4 дня, стоит 1520 форинтов – это около 5 евро. После этого можно кататься сколько угодно. Система там простая – при покупке номер вашего автомобиля вносится в…

  • Future Today

    Вот оно будущее, уже можно потрогать руками более менее простым смертным: На этой картинке видно то, что называется HUD (Heads Up Display), дисплей, проецируемый на лобовое стекло – вещь доступная раньше только пилотам современных истребителей и игроков во всякие игры. Тойота начинает вставлять это в новые лексусы (не очень доступно да) и в новые приусы…

  • Google Crashpad

    Для того, чтобы собрать crashpad под Windows с DLL runtime (/MD /MDd), нужно создать в своём домашнем каталоге (c:/users/konst например) подкаталог .gyp и в него положить файл include.gypi со следующим содержимым: {   ‘target_defaults’: {     ‘configurations’: {       ‘Release’: {         ‘target_conditions’: [           [‘OS==”win”‘, {             ‘msvs_settings’: {                ‘VCCLCompilerTool’: {                   ‘RuntimeLibrary’:…

  • Android x86 emulator и google maps/play services

    Google и intel сделали крутую штуку – образы андроида для x86 atom, которые работают весьма шустро на x86 платформе, в отличие от тормозных armv7. Но счастье резко заканчивается, когда нужно делать приложение, использующее google apis (maps, play services) – x86 образы есть только для чистого андроида. Тем не менее, гугол (который поиск) помогает решить эту…