O livro Game Coding Complete, quarta edição , capítulo 5 ( Inicialização e desligamento do jogo ), seção Verificando memória, contém este interessante exemplo de código:
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;
}
}
Isso levanta algumas questões.
A primeira parte apenas pergunta ao sistema operacional (Windows) quanta RAM física está disponível. A parte curiosa é a segunda, que aloca um grande pedaço de memória e a libera imediatamente:
char *buff = GCC_NEW char[virtualRAMNeeded];
if (buff)
{
delete[] buff;
}
O autor continua explicando:
... essa função aloca e libera imediatamente um enorme bloco de memória. Isso tem o efeito de fazer com que o Windows limpe qualquer lixo acumulado no gerenciador de memória e verifique novamente se você pode alocar um bloco contíguo do tamanho necessário. Se a chamada for bem-sucedida, você basicamente executará o equivalente a uma máquina Zamboni na memória do sistema, preparando-a para o seu jogo atingir o gelo ...
Mas tenho minhas reservas nisso.
"Limpando o lixo acumulado no gerenciador de memória?" Verdade? Se o jogo acabou de começar, não deveria haver lixo?
"Certificando-se de que você pode alocar um bloco contíguo?" No caso muito específico em que você mesmo gerenciará a memória, isso faria algum sentido, mas ainda assim, se você alocar muita memória diretamente do bastão, praticamente impossibilitará a execução de qualquer outro aplicativo o sistema enquanto o seu estiver ligado.
Além disso, não é provável que isso force o sistema operacional a comprometer toda essa memória e, como consequência, despeje muita memória no espaço em disco de troca, retardando muito a inicialização do aplicativo?
Esta é realmente uma boa prática?
operator new
paranullptr
), se me permite dizer. A melhor coisa que você pode fazer com esse livro é acender sua chaminé. Alocar e liberar um grande bloco de memória, é claro , não "limpa" a memória.new
operador global para retornar nulo em vez de jogarbad_alloc
. Se não, então sim, esse código é ainda mais absurdo: Poperator delete
é necessário aceitá-lonullptr
e tratá-lo como não operacional. Qualquer sobrecarga global que não faz isso é interrompida. O que significa que é absurdo de qualquer maneira. Assim como assumir que alocar um enorme bloco de memória e liberá-lo "magicamente" fará algo bom. Na melhor das hipóteses, não causará nenhum dano (provavelmente, uma vez que as páginas nem sequer são tocadas ... caso contrário, poderá trocar algumas páginas do seu conjunto de trabalho que você precisará recarregar mais tarde).Respostas:
Uma coisa que a pré-alocação de uma grande parte da memória pode fazer, se o computador estiver com pouca memória RAM, pode forçar o sistema operacional a liberar espaço extra trocando partes do espaço da memória de outros programas no disco.
Como essa troca geralmente é uma operação muito lenta que congela seu programa enquanto está acontecendo, pode haver alguma vantagem em garantir que, se isso acontecer de qualquer maneira, ocorrerá antes do início do jogo e não no meio da jogabilidade.
Dito isto, forçar uma troca para disco como este não é exatamente 100% confiável:
Em muitas implementações de memória virtual, apenas alocar memória não aciona nenhuma troca; apenas acontece quando você acessa pela primeira vez cada página da memória alocada. (Isso certamente é verdade nas versões modernas do Linux, com a confirmação de memória ativada, como é por padrão; não sei ao certo como as diferentes versões do Windows lidam com isso.)
Portanto, se você realmente deseja que esse código "limpe a memória", provavelmente adicione uma
memset()
chamada ou equivalente para gravar dados em todas as partes da matriz (ou pelo menos nos primeirosphysicalRAMNeeded
bytes dele) antes de liberá-lo.Além disso, forçar o sistema operacional a trocar outros programas da RAM como esse não significa que eles ficarão fora dele. O Windows é um sistema operacional multitarefa e, assim que um desses programas trocados quiser executar novamente e usar seus dados trocados, ele será trocado novamente, potencialmente congelando seu jogo novamente.
Portanto, esse truque geralmente é útil apenas se a RAM estiver sendo consumida por grandes pedaços de dados que não estão sendo acessados ativamente (como documentos grandes deixados abertos em segundo plano em algum software de edição). Os navegadores da Web tendem a ser particularmente problemáticos nesse sentido, pois, mesmo que você não esteja usando uma página da Web aberta em alguma guia em segundo plano, a página ainda pode ter scripts em execução nela que forçam a manutenção na RAM.
(Não são formas de um programa para realmente dizer o OS para bloquear uma parte do seu espaço de memória na RAM e impedem de ser trocados, mas estes normalmente exigem privilégios elevados. Em qualquer caso, o código que você postou não parece ser usando esses métodos.)
Basicamente, esse truque parece útil apenas nessa situação limítrofe relativamente estreita, na qual o usuário teria RAM suficiente para executar seu jogo (e qualquer outro software que esteja sendo executado ativamente em segundo plano) sem trocar, mas atualmente a RAM está cheia de arquivos abertos inativos e não utilizados ou outros dados que podem e devem ser trocados para o disco enquanto o jogo está em execução. Nessas situações, pode ser eficaz para tornar seu jogo mais suave e responsivo, substituindo operações ocasionais de troca pequena durante o jogo por uma única durante a inicialização.
fonte
Não sei quantos anos esse artigo tem, mas eu diria que é bem antigo. No Windows moderno (XP e mais recente, mas especialmente nas versões de 64 bits), eu diria que fazer algo assim terá pouco ou nenhum impacto negativo na inicialização do seu jogo.
Antes de tudo, lembre-se de que o espaço de endereço é virtualizado para cada processo. No que diz respeito ao seu processo, você tem todo o espaço de endereço para si e sempre terá memória contígua (virtual), independentemente de essa memória ser fisicamente contígua ou não.
De fato, se a memória é ou não contígua na memória física não é algo sobre o qual você tem controle. O sistema operacional escolherá como alocar os blocos físicos como achar melhor, e nada que você faça no nível do aplicativo poderá afetar os benefícios (se houver) de ter memória física contígua.
Você precisa se preocupar com a fragmentação da memória virtual se continuar alocando e liberando memória de tamanhos diferentes por um período muito longo. Obviamente, isso é muito menos relevante com um espaço de endereço de 64 bits. Os jogos não costumam rodar há meses, então você provavelmente não precisará se preocupar com isso, a menos que falemos de servidores de jogos de longa duração.
Mesmo se você alocar muita memória de tamanhos totalmente diferentes em uma máquina de 32 bits, as alocações de memória modernas são muito boas, portanto, é improvável que você tenha problemas de fragmentação da memória virtual, a menos que esteja procurando ativamente por elas.
Para ser sincero com você, não sei o que esse escritor está falando. Mesmo que o espaço de endereço tenha sido compartilhado e você tenha recebido um enorme bloco contíguo na memória física, liberando-o, você está dizendo que não se importa mais com ele. Existem outros programas sendo executados em segundo plano, fazendo suas próprias alocações; portanto, mesmo que seu grande malloc tenha sido bem-sucedido, ninguém garantirá que o próximo será bem-sucedido.
Eu acho que é um caso de "funcionou por algum motivo que eu realmente não entendo, então inventei algo para explicá-lo".
fonte