Cosa fa la funzione Sys_PageIn () in Quake?


8

Ho notato che nel processo di inizializzazione del Quake originale viene chiamata la seguente funzione.

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

Penso di non avere abbastanza familiarità con il paging per capire questa funzione. il void * ptr passato alla funzione è un pezzo di memoria recentemente malloc () che ha dimensioni byte grandi. Questa è l'intera funzione - j è una variabile senza riferimento. La mia ipotesi migliore è che il volatile int sys_checksum sta forzando il sistema a leggere fisicamente tutto lo spazio che era appena malloc (), forse per garantire che questi spazi esistano nella memoria virtuale? È giusto? E perché qualcuno dovrebbe farlo? È per qualche motivo antiquato di Win95?

Risposte:


6

La tua ipotesi è fondamentalmente corretta, e viene eseguita come un'ottimizzazione (molto probabilmente; posso solo speculare ovviamente poiché non ho scritto il codice).

Mentre un'applicazione in Windows sembra avere pieno accesso all'intero intervallo di RAM nella macchina (o almeno all'intervallo segnalato dal sistema operativo), in pratica il sistema operativo sta virtualizzando l'accesso di un'applicazione alla memoria fisica effettiva e memorizzerà regioni (pagine) di memoria virtuale su disco quando necessario. Il processo di trasferimento di queste regioni dal disco alla RAM fisica viene spesso chiamato "paging in" (quando si passa da disco a RAM) o "paging out" (quando si passa da RAM a disco).

Disk IO è lento, rispetto alla RAM, quindi evitare il paging è l'ideale per ottenere le massime prestazioni. L'intento di questa funzione sembra essere quello di tentare di ridurre al minimo il paging durante la durata del programma forzando il sistema operativo a sfogliare tutta la memoria all'inizio del programma - la forzatura si ottiene provando a leggere da tutta la memoria.

Presumibilmente Windows 95 aveva un qualche tipo di codice per rilevare e fermare questo comportamento, che il commento suggerisce che viene aggirato leggendo la memoria in un modello particolare. Ha senso che il sistema operativo avrebbe fatto questo, perché forzando una pagina-in come questo costringerà altra memoria processi completo da paging su disco, probabilmente rallentando li giù.

Si può sostenere che questo è un comportamento accettabile per un gioco perché un utente generalmente eseguirà il gioco e non proverà a fare molto multitasking mentre il gioco è attivo, quindi sacrificare le prestazioni di altri processi che potrebbero essere in esecuzione non lo è quel male.

Alcune altre note:

  • Questo genere di cose probabilmente non funzionerà altrettanto bene oggi come probabilmente in Windows 95. La natura degli scheduler del sistema operativo è cambiata molto da allora, quindi non è necessariamente una tecnica che ti suggerirei di adottare se non hai un convincente dati e metriche del profiler per supportare il fatto che il tuo tentativo sia un vantaggio.

  • volatileè un suggerimento per l'implementazione per evitare l'ottimizzazione aggressiva di un oggetto così dichiarato perché tale oggetto potrebbe cambiare attraverso la previsione di previsione dell'implementazione. In altre parole, è come una bandiera "non ottimizzarmi". In questo modo il compilatore, anche se si rende conto che la variabile sostanzialmente non utilizzata in alcun modo significativo, non ottimizzerà le letture dalla memoria in quella variabile come parte del suo passaggio di ottimizzazione.

  • j essere inutilizzati è probabilmente solo una svista.


1

Raymond Chen risponde direttamente a questo in un post successivo sul suo blog The Old New Thing (Maximus Minimius aveva la fonte giusta che risulta solo 3 anni prima per una spiegazione diretta): https://blogs.msdn.microsoft.com/oldnewthing / 20.151.111-00 /? p = 91.972

Ciò che questo codice fa è accedere al blocco di memoria specificato dai parametri ptr e size in un modello insolito: legge byte zero, quindi il byte con un offset di 16 pagine, quindi il byte uno, quindi un byte con un offset di 16 pagine più uno e così via, alternando tra un byte e la sua controparte 16 pagine in anticipo.

Questo modello di accesso specifico in Windows 95 ha sconfitto l'algoritmo di rilevamento "scansione sequenziale della memoria".

Ricordiamo che i computer dell'era Windows 95 avevano 4 MB di RAM. Supponiamo che tu stia lavorando a lungo in un documento. Finalmente hai finito e chiudi la finestra o la minimizzi. Boom, ora il tuo desktop è visibile e la bitmap dello sfondo deve essere cercata. Se lo schermo è 1024 × 768 a 16 bit per pixel, questo risulta a 1,5 MB di memoria. Il paging in 1,5 MB di memoria significa per la bitmap significa espellere 1,5 MB di memoria utilizzata per altre cose, e questa è molta memoria per una macchina che ha solo 4 MB con cui lavorare (specialmente perché gran parte di quei 4 MB appartiene a cose che non è idoneo per il paging). Il fenomeno che abbiamo visto è stato che ridipingere il desktop cancellerebbe la maggior parte della memoria.

E poi la prossima cosa che fai è probabilmente lanciare una nuova applicazione, che coprirà lo sfondo, quindi la memoria dello sfondo non sarà più necessaria. Quindi abbiamo praticamente eliminato tutta la memoria del tuo sistema al fine di gestire un enorme blocco di memoria a cui è stato effettuato l'accesso solo una volta.

Il trucco usato da Windows 95 era guardare il tuo schema di errori di pagina, e se vedeva che stavi facendo un accesso sequenziale alla memoria, ha iniziato a contrassegnare la memoria 16 pagine dietro l'accesso corrente come non di recente accesso . Nel caso di una scansione sequenziale diretta, ciò significa che l'intero buffer scorre ciclicamente attraverso una finestra di memoria di 64 KB, indipendentemente dalle dimensioni del buffer. Con questo trucco, un buffer da 4 MB finisce per consumare solo 64 KB di memoria, invece di utilizzare tutta la memoria del sistema.

La Sys_Page­Infunzione sconfigge specificamente il rilevatore a scansione sequenziale tornando intenzionalmente indietro di 16 pagine e accedendo nuovamente alla pagina. Questo fa sì che venga contrassegnato come usato di recente , contrastando ciò che non è stato utilizzato di recente dal rivelatore a scansione sequenziale. Risultato: le pagine di memoria sono tutte contrassegnate di recente e non sono più i candidati principali per il paging.


0

Risuscitando questo, ho notato di recente questa voce sul sito di Raymond Chen: http://blogs.msdn.com/b/oldnewthing/archive/2012/08/13/10334566.aspx

Perché sono nei crediti di Quake? Non ricordo che cosa ho fatto nello specifico ... il consiglio che ho dato era quasi certamente legato alla gestione della memoria e allo scambio.

Ciò indica che esiste almeno una discreta possibilità che questa funzione sia il risultato del consiglio di Raymond (e quando Raymond Chen dice "devi farlo", c'è almeno una discreta possibilità che abbia ragione).

Al giorno d'oggi è facile da dimenticare, ma nel 1996 il PC gamer medio avrebbe avuto forse 16 MB di RAM, max , e Quake era un mostro assoluto di un programma. In quei giorni i dischi rigidi usati per macinare incessantemente a causa del paging, e l'estrazione di tutta la memoria allocata in questo modo avrebbe (almeno) contribuito a impedire che il file di paging dovesse essere toccato in fase di esecuzione, il che avrebbe potuto portare a uno stallo di qualsiasi cosa fino a un secondo o più.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.