O que a função Sys_PageIn () faz no Quake?

8

Percebi no processo de inicialização do Quake original que a seguinte função é chamada.

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

Acho que não estou familiarizado o suficiente com paginação para entender essa função. o void * ptr passado para a função é um pedaço de memória recentemente malloc () com tamanho de bytes grande. Esta é toda a função - j é uma variável não referenciada. Meu melhor palpite é que o volátil int sys_checksum está forçando o sistema a ler fisicamente todo o espaço que era apenas malloc (), talvez para garantir que esses espaços existam na memória virtual? Isto está certo? E por que alguém faria isso? É por algum motivo antiquado do Win95?

Philip
fonte

Respostas:

6

Seu palpite é basicamente correto e está sendo feito como uma otimização (provavelmente; só posso especular, é claro, porque não escrevi o código).

Embora um aplicativo no Windows pareça ter acesso total a toda a faixa de RAM na máquina (ou pelo menos à faixa relatada pelo sistema operacional), na prática, o sistema operacional está virtualizando o acesso de um aplicativo à memória física real e armazenará regiões (páginas) da memória virtual em disco quando necessário. O processo de transferência dessas regiões do disco para a RAM física geralmente é chamado de "paginação de entrada" (ao passar do disco para a RAM) ou "paginação de saída" (ao passar da RAM para o disco).

O IO do disco é lento, comparado à RAM, e, portanto, evitar paginação é ideal para obter o máximo desempenho. A intenção desta função parece ser a tentativa de minimizar a paginação durante a vida útil do programa, forçando o sistema operacional a paginar toda a memória no início do programa - a forçagem é realizada tentando ler toda a memória.

Presumivelmente, o Windows 95 tinha algum tipo de código para detectar e interromper esse comportamento, o que o comentário sugere que está sendo contornado pela leitura da memória em um padrão específico. Faz sentido que o sistema operacional faça isso, porque forçar uma entrada de página completa como essa forçará a memória de outros processos a serem paginados em disco, provavelmente diminuindo a velocidade.

Pode-se argumentar que esse é um comportamento aceitável para um jogo, porque um usuário geralmente executa apenas o jogo e não tenta executar várias tarefas múltiplas enquanto o jogo está em execução, portanto, sacrificar o desempenho de outros processos que podem estar em execução não é esse mal.

Algumas outras notas:

  • É provável que esse tipo de coisa não funcione tão bem hoje como provavelmente no Windows 95. A natureza dos agendadores de SO mudou bastante desde então, portanto, não é necessariamente uma técnica que eu sugiro que você adote, a menos que seja exigente. dados e métricas do criador de perfil para apoiar o fato de que sua tentativa é um benefício.

  • volatileé uma dica da implementação para evitar a otimização agressiva de um objeto assim declarado, porque esse objeto pode mudar por meios que a implementação não pode prever. Em outras palavras, é como uma bandeira "não me otimize". Dessa forma, o compilador, mesmo que perceba que a variável essencialmente não utilizada de maneira significativa, não otimizará as leituras da memória nessa variável como parte de sua passagem de otimização.

  • j não usar é provavelmente apenas uma supervisão.


fonte
1

Raymond Chen responde a isso diretamente em uma postagem posterior em seu blog The Old New Thing (Maximus Minimius tinha a fonte correta, apenas três anos mais cedo para uma explicação direta): https://blogs.msdn.microsoft.com/oldnewthing / 20151111-00 /? P = 91972

O que esse código faz é acessar o bloco de memória especificado pelos parâmetros ptr e size em um padrão incomum: lê byte zero, depois o byte com um deslocamento de 16 páginas, depois o byte um, depois um byte com um deslocamento de 16 páginas mais uma e assim por diante, alternando entre um byte e sua contraparte 16 páginas à frente.

Esse padrão de acesso específico no Windows 95 derrotou o algoritmo de detecção "varredura de memória sequencial".

Lembre-se de que os computadores da época do Windows 95 tinham 4 MB de RAM. Suponha que você esteja trabalhando em um documento por um longo tempo. Finalmente, você está pronto e fecha a janela ou a minimiza. Boom, agora sua área de trabalho está visível e o bitmap do papel de parede precisa ser paginado. Se a tela tiver 1024 × 768 a 16 bits por pixel, isso significa 1,5 MB de memória. Paginar em 1,5 MB de memória significa que o bitmap significa expulsar 1,5 MB de memória sendo usado para outras coisas, e isso é muita memória para uma máquina que possui apenas 4 MB para trabalhar (especialmente porque muitos desses 4 MB pertencem a outras coisas) que não é elegível para ser paginado). O fenômeno que vimos foi que repintar sua área de trabalho liberaria a maior parte de sua memória.

E então a próxima coisa que você faz é provavelmente iniciar um novo aplicativo, que cubra o papel de parede, para que a memória do papel de parede não seja mais necessária. Então, basicamente eliminamos toda a memória do seu sistema para lidar com um enorme bloco de memória que foi acessado apenas uma vez.

O truque usado pelo Windows 95 era observar seu padrão de falhas de página e, se percebesse que você estava fazendo acesso seqüencial à memória, começou a marcar a memória como 16 páginas atrás do acesso atual como não acessado recentemente . No caso de uma varredura seqüencial direta, isso significa que todo o buffer percorre uma janela de 64 KB de memória, independentemente do tamanho do buffer. Com esse truque, um buffer de 4 MB acaba consumindo apenas 64 KB de memória, em vez de usar toda a memória do sistema.

A Sys_Page­Infunção anula especificamente o detector de varredura seqüencial, voltando intencionalmente 16 páginas e acessando a página novamente. Isso faz com que ele seja marcado como usado recentemente , neutralizando o usado recentemente que o detector de varredura seqüencial havia feito. Resultado: todas as páginas de memória são marcadas como usadas recentemente e não são mais as candidatas principais a serem paginadas.

Tyler Szabo
fonte
0

Ressuscitando isso, notei recentemente esta entrada no site de Raymond Chen: http://blogs.msdn.com/b/oldnewthing/archive/2012/08/13/10334566.aspx

Por que estou nos créditos do Quake? Não me lembro do que fiz especificamente ... o conselho que dei foi quase certamente relacionado ao gerenciamento e à troca de memória.

Isso indica que há pelo menos uma possibilidade decente de que essa função seja o resultado do conselho de Raymond (e quando Raymond Chen diz "você precisa fazer isso", há pelo menos uma possibilidade decente de que ele esteja certo).

Hoje em dia é fácil esquecer, mas em 1996 o PC médio dos jogadores teria talvez 16mb de RAM, no máximo , e Quake era um monstro absoluto de um programa. Naqueles dias, os discos rígidos costumavam ser removidos incansavelmente devido à paginação, e puxar toda a memória alocada dessa maneira ajudaria (pelo menos) a impedir que o arquivo de paginação fosse tocado em tempo de execução, o que poderia levar a uma paralisação. qualquer coisa até um segundo ou mais.

Maximus Minimus
fonte