Что делает функция Sys_PageIn () в Quake?

8

Я заметил, что в процессе инициализации оригинального Quake вызывается следующая функция.

volatile int sys_checksum;

//  **lots of code**

void Sys_PageIn(void *ptr, int size)
{
    byte *x;
    int j,m,n;
//touch all memory to make sure its there.  The 16-page skip is to
//keep Win 95 from thinking we're trying to page ourselves in (we are
//doing that, of course, but there's no reason we shouldn't)
    x = (byte *)ptr;

    for (n=0 ; n<4 ; n++)
    {
        for (m=0; m<(size - 16 * 0x1000) ; m += 4)
        {
            sys_checksum += *(int *)&x[m];
            sys_checksum += *(int *)&x[m + 16 * 0x10000];
        }
    }
}

Я думаю, что я просто недостаточно знаком с пейджингом, чтобы понять эту функцию. void * ptr, переданный в функцию - это недавно кусок памяти malloc (), который имеет большой размер в байтах. Это целая функция - переменная, на которую нет ссылок. Мое лучшее предположение состоит в том, что volatile int sys_checksum заставляет систему физически считывать все пространство, которое было просто malloc () d, возможно, чтобы гарантировать, что эти пространства существуют в виртуальной памяти? Это правильно? И зачем кому-то это делать? Это по какой-то устаревшей причине Win95?

Филипп
источник

Ответы:

6

Ваше предположение в основном верно, и это делается как оптимизация (скорее всего, я могу только строить догадки, поскольку я не писал код).

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

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

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

Можно утверждать, что это приемлемое поведение для игры, потому что пользователь, как правило, только запускает игру и не пытается выполнять многозадачность во время игры, поэтому жертвовать производительностью других процессов, которые могут выполняться, не это зло

Некоторые другие заметки:

  • Подобные вещи вряд ли будут работать сегодня так же хорошо, как в Windows 95. Характер планировщиков ОС с тех пор сильно изменился, поэтому я не рекомендую применять этот метод, если у вас нет убедительных данные и метрики профилировщика, подтверждающие тот факт, что ваша попытка приносит пользу.

  • volatileявляется подсказкой для реализации, чтобы избежать агрессивной оптимизации объекта, объявленного таким образом, потому что этот объект может измениться из-за того, что реализация не может предсказать. Другими словами, это как флаг «не оптимизируй меня». Таким образом, компилятор, даже если он осознает, что переменная практически не используется каким-либо существенным образом, не будет оптимизировать чтение из памяти в эту переменную в рамках этапа оптимизации.

  • j быть неиспользованным, вероятно, просто недосмотр.


источник
1

Рэймонд Чен отвечает на это прямо в последующем сообщении в своем блоге «Старое новое» (у Максимуса Минимиуса был правильный источник, выясняется, что для прямых объяснений он всего на три года раньше): https://blogs.msdn.microsoft.com/oldnewthing / 20151111-00 /? р = девяносто одна тысяча девятьсот семьдесят две

Этот код выполняет доступ к блоку памяти, указанному параметрами ptr и size, в необычном шаблоне: он читает нулевой байт, затем байт со смещением 16 страниц, затем один байт, затем байт со смещением 16 страниц плюс один и т. д., чередующийся между байтом и его аналогом на 16 страниц вперед.

Этот специфический шаблон доступа в Windows 95 побеждал алгоритм обнаружения «последовательного сканирования памяти».

Напомним, что компьютеры в эпоху Windows 95 имели 4 МБ ОЗУ. Предположим, вы долго работали над документом. Наконец, вы сделали, и вы закрываете окно или сверните его. Бум, теперь ваш рабочий стол виден, и растровые изображения обоев должны быть разбиты на страницы. Если ваш экран имеет разрешение 1024 × 768 при 16 битах на пиксель, это дает 1,5 МБ памяти. Пейджинг в 1,5 МБ памяти означает, что для растрового изображения нужно выкинуть 1,5 МБ памяти, используемой для других задач, и это достаточно памяти для машины, с которой нужно работать только 4 МБ (тем более, что большая часть этих 4 МБ принадлежит материалам). это не имеет права быть выгруженным). Феномен, который мы видели, состоял в том, что перекраска вашего рабочего стола вымывает большую часть вашей памяти.

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

Уловка, которую использовала Windows 95, состояла в том, чтобы наблюдать ваш шаблон ошибок страниц, и если она увидела, что вы выполняете последовательный доступ к памяти, она начала помечать память на 16 страниц за текущим доступом как доступ к которой не было недавно. . В случае прямого последовательного сканирования это означает, что весь буфер циклически перебирает окно памяти размером 64 КБ независимо от размера буфера. С этим приемом буфер 4 МБ в итоге потребляет только 64 КБ памяти, в отличие от использования всей памяти в вашей системе.

Эта Sys_Page­Inфункция специально отключает детектор последовательного сканирования, преднамеренно возвращаясь назад на 16 страниц и снова получая доступ к странице. Это приводит к тому, что он помечается как недавно использованный , что противодействует неиспользованному недавно, что сделал детектор последовательного сканирования. Результат: все страницы памяти помечены как недавно использованные и больше не являются первыми кандидатами на удаление.

Тайлер Сабо
источник
0

Воскресив это, я недавно заметил эту запись на сайте Рэймонда Чена: http://blogs.msdn.com/b/oldnewthing/archive/2012/08/13/10334566.aspx

Почему я в кредитах Quake? Я не помню, что именно я делал конкретно ... совет, который я давал, почти наверняка был связан с управлением памятью и обменом.

Это указывает на то, что, по крайней мере, есть приличная вероятность того, что эта функция является результатом совета Рэймонда (и когда Рэймонд Чен говорит «вам нужно сделать это», есть, по крайней мере, приличная вероятность, что он прав).

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

Максимус Минимус
источник