¿Qué hace la función Sys_PageIn () en Quake?

8

He notado en el proceso de inicialización del Quake original que se llama la siguiente función.

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];
        }
    }
}

Creo que no estoy lo suficientemente familiarizado con la paginación para comprender esta función. el void * ptr pasado a la función es una pieza de memoria malloc () recientemente que tiene un tamaño de bytes grande. Esta es toda la función: j es una variable sin referencia. Mi mejor conjetura es que el volátil int sys_checksum está obligando al sistema a leer físicamente todo el espacio que era solo malloc () ', ¿tal vez para garantizar que estos espacios existan en la memoria virtual? ¿Es esto correcto? ¿Y por qué alguien haría esto? ¿Es por alguna razón anticuada de Win95?

Philip
fuente

Respuestas:

6

Su suposición es básicamente correcta, y se está haciendo como una optimización (muy probablemente; solo puedo especular, por supuesto, ya que no escribí el código).

Mientras que una aplicación en Windows parece tener acceso completo a todo el rango de RAM en la máquina (o al menos el rango que le informa el sistema operativo), en la práctica el sistema operativo está virtualizando el acceso de una aplicación a la memoria física real y almacenará regiones (páginas) de la memoria virtual al disco cuando sea necesario. El proceso de transferir estas regiones del disco a la RAM física a menudo se denomina "paginación" (cuando se va de disco a RAM) o "paginación" (cuando se va de RAM a disco).

Disk IO es lento, en comparación con RAM, por lo que evitar la paginación es ideal para lograr el máximo rendimiento. La intención de esta función parece ser intentar minimizar la paginación durante la vida útil del programa al forzar al sistema operativo a buscar toda la memoria al inicio del programa; el forzamiento se logra al intentar leer desde toda la memoria.

Presumiblemente, Windows 95 tenía algún tipo de código para detectar y detener este comportamiento, que el comentario sugiere que se está burlando al leer la memoria en un patrón particular. Tiene sentido que el sistema operativo haga esto, porque forzar una entrada de página completa como esta obligará a otros procesos de memoria a paginarse en el disco, probablemente disminuyendo su velocidad.

Se puede argumentar que este es un comportamiento aceptable para un juego porque un usuario generalmente solo ejecutará el juego y no intentará realizar muchas tareas múltiples mientras el juego está activo, por lo que sacrificar el rendimiento de otros procesos que pueden estar ejecutándose no es que maldad

Algunas otras notas:

  • Es probable que este tipo de cosas no funcione tan bien hoy como lo hizo en Windows 95. La naturaleza de los programadores del sistema operativo ha cambiado mucho desde entonces, por lo que no es necesariamente una técnica que sugeriría que adopte a menos que tenga Datos y métricas del generador de perfiles para respaldar el hecho de que su intento es un beneficio.

  • volatilees una pista a la implementación para evitar la optimización agresiva de un objeto así declarado porque ese objeto podría cambiar a través de medios que no se puede esperar que la implementación prediga. En otras palabras, es como una bandera de "no me optimices". De esta manera, el compilador, incluso si se da cuenta de que la variable esencialmente no utilizada de manera significativa, no optimizará las lecturas de la memoria en esa variable como parte de su pase de optimización.

  • j estar sin usar es probablemente solo un descuido.


fuente
1

Raymond Chen responde esto directamente en una publicación posterior en su blog The Old New Thing (Maximus Minimius tenía la fuente correcta, resulta que solo 3 años antes para una explicación directa): https://blogs.msdn.microsoft.com/oldnewthing / 20151111-00 /? P = 91972

Lo que hace este código es acceder al bloque de memoria especificado por los parámetros ptr y size en un patrón inusual: lee el byte cero, luego el byte en un desplazamiento de 16 páginas, luego el byte uno, luego un byte en un desplazamiento de 16 páginas más uno, y así sucesivamente, alternando entre un byte y su contraparte 16 páginas adelante.

Este patrón de acceso específico en Windows 95 derrotó al algoritmo de detección de "exploración secuencial de memoria".

Recordemos que las computadoras en la era de Windows 95 tenían 4 MB de RAM. Suponga que estuvo trabajando en un documento durante mucho tiempo. Finalmente, ha terminado y cierra la ventana o la minimiza. Boom, ahora su escritorio está visible y el mapa de bits del fondo de pantalla debe ser paginado. Si su pantalla es 1024 × 768 a 16 bits por píxel, eso equivale a 1,5 MB de memoria. Paginar en 1.5 MB de memoria significa que el mapa de bits significa eliminar 1.5 MB de memoria que se usa para otras cosas, y eso es mucha memoria para una máquina que solo tiene 4 MB para trabajar (especialmente porque gran parte de esos 4 MB pertenecen a cosas que no es elegible para ser paginado). El fenómeno que vimos fue que volver a pintar su escritorio eliminaría la mayor parte de su memoria.

Y luego, lo siguiente que debe hacer es probablemente iniciar una nueva aplicación, que cubrirá el fondo de pantalla, por lo que la memoria del fondo de pantalla ya no será necesaria. Básicamente, purgamos toda la memoria en su sistema para manejar un gran bloque de memoria al que se accedió solo una vez.

El truco que usó Windows 95 fue observar su patrón de fallas de página, y si vio que estaba haciendo un acceso secuencial a la memoria, comenzó a marcar la memoria 16 páginas detrás del acceso actual como no accedido recientemente . En el caso de una exploración secuencial directa, esto significa que todo el búfer pasa por una ventana de memoria de 64 KB, independientemente del tamaño del búfer. Con este truco, un búfer de 4 MB termina consumiendo solo 64 KB de memoria, en lugar de usar toda la memoria de su sistema.

La Sys_Page­Infunción específicamente elimina el detector de exploración secuencial al retroceder intencionalmente 16 páginas y acceder nuevamente a la página. Esto hace que se marque como utilizado recientemente , contrarrestando el no utilizado recientemente que había hecho el detector de exploración secuencial. Resultado: todas las páginas de memoria están marcadas como usadas recientemente y ya no son las principales candidatas para la paginación.

Tyler Szabo
fuente
0

Resucitando esto, noté esta entrada en el sitio de Raymond Chen recientemente: http://blogs.msdn.com/b/oldnewthing/archive/2012/08/13/10334566.aspx

¿Por qué estoy en los créditos de Quake? No recuerdo qué fue lo que hice específicamente ... el consejo que di estuvo casi seguramente relacionado con el manejo y el intercambio de memoria.

Esto indica que hay al menos una posibilidad decente de que esta función sea el resultado del consejo de Raymond (y cuando Raymond Chen dice "usted necesita hacer esto", existe al menos una posibilidad decente de que tenga razón).

Es fácil olvidarlo hoy en día, pero en 1996 la PC de jugador promedio habría tenido quizás 16 MB de RAM, máximo , y Quake era un monstruo absoluto de un programa. En aquellos días, los discos duros solían triturarse sin descanso debido a la paginación, y extraer toda la memoria asignada de esta manera habría ayudado (al menos) a evitar que el archivo de la página tuviera que ser tocado en el tiempo de ejecución, lo que podría haber llevado a un bloqueo de cualquier cosa hasta un segundo o más.

Maximus Minimus
fuente