Распределяет ли и освобождает ли огромный кусок памяти при запуске «очистку памяти»?


18

Книга Game Coding Complete, четвертое издание , глава 5 ( Инициализация и завершение игры ), раздел Проверка памяти, содержит этот интересный пример кода:

bool CheckMemory(const DWORDLONG physicalRAMNeeded, const DWORDLONG virtualRAMNeeded)
{
    MEMORYSTATUSEX status; 
    GlobalMemoryStatusEx(&status);
    if (status.ullTotalPhys < physicalRAMNeeded) 
    {
        // you don’t have enough physical memory. Tell the player to go get a 
        // real computer and give this one to his mother. 
        GCC_ERROR("CheckMemory Failure: Not enough physical memory."); 
        return false;
    }
    // Check for enough free memory.
    if (status.ullAvailVirtual < virtualRAMNeeded) 
    {
        // you don’t have enough virtual memory available.
        // Tell the player to shut down the copy of Visual Studio running in the 
        // background, or whatever seems to be sucking the memory dry. 
        GCC_ERROR("CheckMemory Failure: Not enough virtual memory.");
        return false;
    }

    char *buff = GCC_NEW char[virtualRAMNeeded]; 
    if (buff)
    {
        delete[] buff;
    }
    else
    {
        // even though there is enough memory, it isn't available in one
        // block, which can be critical for games that manage their own memory 
        GCC_ERROR("CheckMemory Failure: Not enough contiguous memory."); 
        return false;
    }
}

Это поднимает некоторые вопросы.

Первая часть просто спрашивает ОС (Windows), сколько физической памяти доступно. Любопытная часть - вторая, которая выделяет огромный кусок памяти и освобождает ее сразу же:

char *buff = GCC_NEW char[virtualRAMNeeded]; 
if (buff)
{
    delete[] buff;
}

Автор продолжает объяснять:

... эта функция выделяет и немедленно освобождает огромный блок памяти. Это приводит к тому, что Windows очищает любой мусор, накопленный в диспетчере памяти, и дважды проверяет, можно ли выделять непрерывный блок по мере необходимости. Если вызов успешен, вы, по сути, запускаете эквивалент машины Zamboni через память вашей системы, готовя ее к тому, что ваша игра выйдет на лед ...

Но у меня есть оговорки на этот счет.

"Очистка мусора, накопившегося в диспетчере памяти?" В самом деле? Если игра только началась, разве не должно быть мусора?

"Убедитесь, что вы можете выделить непрерывный блок?" В очень конкретном случае, когда вы собираетесь управлять памятью самостоятельно, это имело бы какой-то смысл, но, тем не менее, если вы действительно выделяете много памяти прямо на летучей мыши, вы в значительной степени лишаете возможности запуска любого другого приложения система пока ваша включена.

Кроме того, разве это не заставит ОС выделять всю эту память и, как следствие, высвободить много памяти в пространство подкачки, что сильно замедлит запуск вашего приложения?

Это действительно хорошая практика?


3
Большинство современных ОС ничего не делают, когда приложение выделяет большую область памяти, они используют оптимистичное распределение и фактически ничего не делают, пока вы не заполняете память. Я не могу себе представить, что это делает что-то большее, чем быть потенциально медленным
— неактивным

Очищает ли длинная полоса земли в джунглях, вырезает радио, наушники и т. Д. Из-за того, что самолеты приземляются и доставляют припасы?
— Дэн Нили,

10
Game Coding Complete содержит много бессмыслицы в сочетании с множеством непонятных C ++ и C-that-look-a-чуть-чуть-как-C ++ (также продемонстрированных в этом примере, проверяя результат operator newдля nullptr), если вы позволите мне сказать. Лучшее, что вы можете сделать с этой книгой, это зажечь дымоход. Выделение и освобождение большого блока памяти, конечно , не «очищает» память.
— Деймон

@ Дэймон, я не проверял, но я подозреваю, что они по крайней мере перегружают глобальный newоператор, чтобы возвращать ноль вместо броска bad_alloc. Если они этого не сделали, то да, этот код еще более бессмысленный: P
— glampert

1
@glampert: Даже если предположить, что это так, operator deleteтребуется принять nullptrи рассматривать его как неоперативный. Любая глобальная перегрузка, которая не делает этого, сломана. Что означает, что это бессмысленно в любом случае. Точно так же, как предположение, что выделение огромного блока памяти и освобождение его «волшебным образом» принесут пользу. В лучшем случае это не принесет никакого вреда (скорее всего, поскольку страницы даже не затрагиваются ... в противном случае это может поменять некоторые страницы из вашего рабочего набора, которые вам необходимо будет перезагрузить позже).
— Деймон

Ответы:


12

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

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

Тем не менее, принудительное переключение на диск не является абсолютно надежным:

  • Во многих реализациях виртуальной памяти простое распределение памяти фактически не вызывает никакой подкачки; скорее это происходит только при первом доступе к каждой странице выделенной памяти. (Это, безусловно, верно в современных версиях Linux с включенной перегрузкой памяти, как это происходит по умолчанию; я не знаю точно, как разные версии Windows справляются с этим.)

    Таким образом, если вы действительно хотите, чтобы этот код «очищал память», вам, вероятно, следует добавить memset()вызов или его эквивалент для фактической записи данных в каждую часть массива (или, по крайней мере, в его первые physicalRAMNeededбайты) перед его освобождением.

  • Кроме того, принуждение ОС к замене других программ из оперативной памяти таким образом не означает, что они останутся вне этой памяти. Windows является многозадачной ОС, и как только одна из этих замененных программ захочет снова запустить и использовать свои замененные данные, она снова будет заменена, что потенциально может снова заморозить вашу игру.

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

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

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


1
Исходя из моих ограниченных знаний о низкоуровневом управлении памятью, я могу сказать, что решение о том, стоит ли менять конкретную страницу или нет, гораздо сложнее и учитывает гораздо больше факторов, чем просто объем запрашиваемой памяти и объем доступной памяти. , Упрощать это и вообще полагаться на предположения не очень мудро.
— Панда Пижама

3
Я думаю, что важно помнить, что все это предположения, которые могут работать, а не работать или казаться работающими, но по совершенно не связанным причинам. Насколько я знаю, ни одно из поведений, указанных в этом ответе, не задокументировано, и было бы неразумно полагаться на них. Обновление ОС, изменение настроек, изменение компилятора или почти все, что угодно, могут заставить это перестать работать или даже оказать негативное влияние на производительность.
— Панда Пижама

26

Я не знаю, сколько лет этой записи, но я бы сказал, что она довольно старая. Я бы сказал, что в современных Windows (XP и новее, но особенно в 64-битных версиях) выполнение подобных действий мало повлияет на запуск вашей игры.

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

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

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

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

Если честно, я не знаю, о чем этот писатель. Даже если адресное пространство было разделено, и вы получили огромный непрерывный блок в физической памяти, освобождая его, вы говорите, что вам это больше не нужно. Есть другие программы, работающие в фоновом режиме, которые выполняют свои собственные выделения, поэтому, даже если ваш огромный malloc преуспел, никто не гарантирует, что следующая будет также успешной.

Я думаю, что это тот случай, когда «это сработало по какой-то причине, которую я не совсем понимаю, поэтому я придумал что-то, чтобы объяснить это».


10
Оперативная память не похожа на вращающийся жесткий диск, где наличие смежных блоков в любом случае «лучше», чем нет. Это неправда. непрерывный доступ к ОЗУ на порядок быстрее, чем произвольный доступ. Микросхемы ОЗУ разбиты на секции, для которых существует определенная задержка переключения, и, что более важно, пропуски кеша L1 и L2 сильно повлияют на вашу производительность, когда ваша память будет разбросана.
— CaptainCodeman

@CaptainCodeman: Я должен признать, что это выходит за рамки моей области знаний, но в любом случае, как программист по Windows-приложениям, вы не контролируете, является ли ваша память физически смежной или нет. Просьба об огромном блоке памяти мало что изменит.
— Панда Пижама

@CaptainCodeman на самом деле имеет непрерывный виртуальный блок, находящийся в непрерывном моде 2 ^ N блок в ОЗУ ускорит его из-за выделения строк кэша
— трещотка урод

8
@CaptainCodeman Да, последовательный доступ к DRAM быстрее, но мы говорим о виртуальном-> физическом отображении, которое происходит на страницах размером 4 КБ (или более), поэтому то, является ли это отображение последовательным или нет, не должно сильно влиять на память скорость чтения. Кроме того, предварительная выборка из кеша и тому подобное работает в виртуальном адресном пространстве, а не в физическом.
— Натан Рид

1
@ NatanReed Справедливо, и спасибо за разъяснения. Я только что говорил, что скорость доступа к ОЗУ не совсем одинакова во всем пространстве памяти, как было предложено.
— CaptainCodeman
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.