Як ви готуєтесь до умов пам'яті?


18

Це може бути легко для ігор з чітко визначеним масштабом, але питання полягає в іграх з пісочницею, де гравцеві дозволяється створювати і будувати що завгодно .

Можливі методи:

  • Використовуйте пули пам'яті з верхньою межею.
  • Періодично видаляйте об'єкти, які більше не потрібні.
  • Виділіть додатковий об'єм пам'яті на початку, щоб вона могла бути звільнена пізніше як механізм відновлення. Я б сказав, що близько 2-4 Мб.

Це, швидше за все, відбувається на мобільних / консольних платформах, де пам'ять зазвичай обмежена на відміну від ПК на 16 Гб. Я припускаю, що ви маєте повний контроль над розподілом пам’яті / розсилкою пам’яті та не займаєтесь збиранням сміття. Ось чому я мічу це як C ++.

Зауважте, що я не говорю про Ефективний пункт C ++ Пункт 7 "Будьте готові до умов, що не входять у пам'ять" , хоча це є актуальним, я хотів би побачити відповідь, пов’язану з розвитком гри, де ви, як правило, більше контролюєте, що саме відбувається.

Підсумовуючи питання, як ви готуєтесь до умов пам'яті для ігор в пісочниці, коли ви орієнтуєтесь на платформу з обмеженою консоллю / мобільним пам'яттю?


Не вдалося виділити пам'ять досить рідко в сучасних операційних системах ПК, оскільки вони автоматично підкачуються на жорсткий диск, коли не вистачає фізичної оперативної пам’яті. Проте ситуацію, якої слід уникати, оскільки заміна відбувається значно повільніше, ніж фізична оперативна пам’ять, і сильно вплине на продуктивність.
Філіп

@Philipp так, я знаю. Але моє запитання більше стосується пристроїв з обмеженою пам'яттю, таких як консолі та мобільні телефони, я думаю, що я це згадав.
concept3d

Це досить широке запитання (і своєрідне опитування щодо того, як це написано). Чи можете ви трохи звузити сферу, щоб бути більш конкретними до однієї ситуації?
MichaelHouse

@ Byte56 Я редагував це питання. Я сподіваюся, що зараз він має більш визначені сфери.
concept3d

Відповіді:


16

Як правило, ви не обробляєте поза пам'яттю. Єдиним розумним варіантом програмного забезпечення, такого великого і складного, як гра, є якнайшвидше вийти з ладу / затвердити / закінчити в алокаторі пам'яті (особливо в налагодженнях налагодження). Умови поза пам'яті тестуються та обробляються в деяких основних системних програмних або серверних програм, в деяких випадках, але зазвичай не в інших місцях.

Якщо у вас є верхній ковпачок пам'яті, ви натомість просто переконайтеся, що вам ніколи не потрібно більше, ніж цей об'єм пам'яті. Наприклад, ви можете тримати максимальну кількість дозволених NPC одночасно, і просто припинити нерестувати нові несуттєві NPC, як тільки ця шапка буде натиснута. Для найважливіших NPC ви можете або замінити їх на неістотні, або мати окремий пул / кришку для основних NPC, які ваші дизайнери знають створити (наприклад, якщо у вас може бути лише 3 основних NPCsa, дизайнери не поставлять більше 3 в область / шматок - хороші інструменти допоможуть дизайнерам зробити це належним чином, і тестування важливо, звичайно,).

Дійсно хороша потокова система також важлива особливо для пісочницьких ігор. Вам не потрібно зберігати всі NPC та елементи в пам'яті. Під час переміщення по шматочках світу нові шматки будуть передаватися в потоки, а старі шматки витікатимуть. Вони, як правило, включають NPC та предмети, а також місцевість. Маючи на увазі цю систему, слід встановити обмеження для проектування та інженерних обмежень, знаючи, що щонайбільше X старих шматочків буде зберігатися навколо та активно завантажуватись Y нові шматки, тому в грі потрібно мати місце для збереження всіх дані X + Y + 1 шматки пам’яті.

У деяких іграх намагаються впоратися із ситуаціями, що не знаходяться в пам’яті, із підходом у два проходи. Маючи на увазі, що в більшості ігор є багато технічно непотрібних кешованих даних (скажімо, старих фрагментів, згаданих вище), і розподіл пам'яті може зробити щось на кшталт:

allocate(bytes):
  if can_allocate(bytes):
    return internal_allocate(bytes)
  else:
    warning(LOW_MEMORY)
    tell_systems_to_dump_caches()

    if can_allocate(bytes):
      return internal_allocate(bytes)
    else:
      fatal_error(OUT_OF_MEMORY)

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

Ви також можете розглянути можливість скидання копій деяких даних з високою роздільною здатністю, наприклад, ви можете скидати рівні текстур мипмапи більш високої роздільної здатності, якщо у вас не вистачає пам'яті GPU (або будь-якої пам'яті в архітектурі спільної пам'яті). Зазвичай для цього потрібно багато архітектурних робіт, щоб зробити його вартим.

Зауважте, що деякі дуже необмежені ігри з пісочницею можуть бути досить просто розбиті, навіть на ПК (пам’ятайте, що загальні 32-бітні програми мають обмеження в 2-3 ГБ адресного простору, навіть якщо у вас ПК із 128 ГБ оперативної пам’яті; 64- бітова ОС і апаратне забезпечення дозволяють одночасно запускати більше 32-бітних додатків, але нічого не вдається зробити, щоб 32-бітний двійковий файл мав більший адресний простір). Зрештою, у вас або дуже гнучкий ігровий світ, який потребує необмеженого простору пам’яті для роботи у кожному випадку, або у вас дуже обмежений і контрольований світ, який завжди ідеально працює в обмеженій пам’яті (або щось десь посередині).


+1 за цю відповідь. Я написав дві системи, які працюють у стилі Шона та дискретні пули пам’яті, і обидві вони добре працювали у виробництві. Спочатку був спаунер, який відкочував вихід на кривій до максимального відключення, щоб гравець ніколи не помітив раптового зменшення (думав, що загальна пропускна здатність знизиться на цю норму безпеки). Другий був пов'язаний з шматками в тому, що невдале виділення призвело б до очищення та перерозподілу. Я вважаю, що ** дуже обмежений і контрольований світ, який завжди ідеально працює в обмеженій пам'яті **, є життєво важливим для будь-якого довго працюючого клієнта.
Патрік Х'юз

+1 для згадки про те, що ви є настільки агресивними при використанні помилок у налагодженнях. Пам'ятайте, що на апаратній консолі налагодження іноді ви маєте доступ до більшої кількості ресурсів, ніж роздрібний. Ви можете захотіти імітувати ці умови на апаратному забезпеченні розробника, розподіляючи об'єкти налагодження виключно в адресному просторі над тим, що мали б пристрої роздрібної торгівлі, і збоїв, коли використовується роздрібний еквівалентний адресний простір.
FlintZA

5

Зазвичай програма тестується на цільовій платформі з найгіршими сценаріями, і ви завжди будете готові до платформи, на яку ви орієнтовані. В ідеалі додаток ніколи не повинен виходити з ладу, окрім оптимізації для конкретних пристроїв, є мало варіантів, коли ви стикаєтеся з попередженням про низьку пам'ять.

Найкращою практикою є наявність попередньо розподілених басейнів, і гра з самого початку використовує всю необхідну пам'ять. Якщо ваша гра має максимум 100 одиниць, ніж пул на 100 одиниць, і це все. Якщо 100 одиниць перевищують вимоги до пам’яті для одного цільового пристрою, ви можете оптимізувати пристрій, щоб використовувати менше пам’яті або змінити дизайн на максимум 90 одиниць. Не повинно бути випадків, коли ви можете будувати необмежені речі, завжди має бути обмеження. Було б дуже погано використовувати гру з пісочницею newдля кожного екземпляра, тому що ви ніколи не можете передбачити використання пам'яті, а крах - це набагато гірше, ніж обмеження.

Крім того, ігровий дизайн завжди повинен мати на увазі найменш орієнтовані пристрої, тому що якщо ви базуєте свій дизайн з "необмеженими" речами в ньому, то вирішити проблеми з пам'яттю або змінити дизайн згодом буде набагато складніше.


1

Ну, ви можете виділити близько 16 МіБ (лише щоб бути впевненим на 100%) при запуску або навіть в .bssчас компіляції, і використовувати "безпечний розподільник", з підписом на зразок inline __attribute__((force_inline)) void* alloc(size_t size)( __attribute__((force_inline))є mingw-w64атрибут GCC /, який змушує вбудовувати розділи критичного коду навіть якщо оптимізація вимкнена, хоча вони повинні бути включені для ігор) замість mallocцього намагається, void* result = malloc(size)і якщо вона не вдасться, киньте кеші, звільніть запасну пам’ять (або скажіть інший код, щоб використовувати .bssріч, але це не вдається для цієї відповіді) і очистіть збережені дані (збережіть світ на диску, якщо ви використовуєте Minecraft-подібну концепцію шматочків, назвіть щось на зразок saveAllModifiedChunks()). Потім, якщо malloc(16777216)(виділення цих 16 МіБ знову) не вдасться (знову замініть аналоговим на .bss), припиніть гру та покажітьMessageBox(NULL, "*game name* couldn't continue because of lack of free memory, but your world was safely saved. Try closing background applications and restarting the game", "*Game name*: out of memory", MB_ICONERROR)або альтернатива платформи. Збираємо все це разом:

__attribute__((force_inline)) void* alloc(size_t size) {
    void* result = malloc(size); // Attempt to allocate normally
    if (!result) { // If the allocation failed...
        if (!reserveMemory) std::_Exit(); // If alloc() was called from forceFullSave() or reportOutOfMemory() and we again can't allocate, just quit, something is stealing all our memory. If we used the .bss approach, this wouldn't've been necessary.
        free(reserveMemory); // Global variable, pointer to the reserve 16 MiB allocated on startup
        forceFullSave(); // Saves the game
        reportOutOfMemory(); // Platform specific error message box code
        std::_Exit(); // Close silently
    } else return result;
}

Ви можете використовувати аналогічне рішення з тим, std::set_new_handler(myHandler)де myHandlerце void myHandler(void)називається, коли newне вдається:

void newerrhandler() {
    if (!reserveMemory) std::_Exit(); // If new was called from forceFullSave() or reportOutOfMemory() and we again can't allocate, just quit, something is stealing all our memory. If we used the .bss approach, this wouldn't've been necessary.
    free(reserveMemory); // Global variable, pointer to the reserve 16 MiB allocated on startup
    forceFullSave(); // Saves the game
    reportOutOfMemory(); // Platform specific error message box code
    std::_Exit(); // Close silently
}

// In main ()...
std::set_new_handler(newerrhandler);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.