Como fazer o perfil e o pool de memória por sistema?

8

Eu tenho interesse em criar perfis e manter um pool de memória gerenciada para cada subsistema, para que eu possa obter estatísticas sobre a quantidade de memória que está sendo usada em algo como sons ou gráficos. No entanto, qual seria um design que funcione para fazer isso? Eu estava pensando em usar vários alocadores e apenas usar um por subsistema, no entanto, isso resultaria em variáveis ​​globais para meus alocadores (ou pelo menos me parece). Outra abordagem que vi / foi sugerida é simplesmente sobrecarregar novas e passar um alocador para um parâmetro.

Eu tinha uma pergunta semelhante sobre o stackoverflow aqui com uma recompensa, no entanto, parece que talvez eu fosse muito vaga ou simplesmente não há pessoas suficientes com conhecimento no assunto.

chadb
fonte
Removida minha resposta. Voltei e li os artigos e os pools usados ​​para rastrear alocações por sistema não fazem parte desses artigos (embora tenham sido baseados nisso). Se eu puder encontrar esses específicos novamente, vincularei-os! Desculpa!
James
11
Dê uma olhada neste post de Jesus de Santos Garcia. Nele, ele discute o rastreamento de memória por subsistema e o uso de vários alocadores para vários requisitos de memória.
7
Não fique obcecado com paradigmas teóricos. Se você precisar de funcionalidade e dados globalmente, não há nada errado com os globais. Já existem várias funções globais como new / delete / malloc / free. Basta adicionar o que você precisa para fazer o trabalho.
Maik Semder

Respostas:

1

Definitivamente, é uma pergunta que pode parecer vaga para alguns;)

Mas acho que sei de onde você vem.

Você tem um milhão de opções para decidir como implementar isso. Algumas dessas opções devem girar em torno das plataformas de destino e dos objetivos gerais de design. Essas considerações romperão qualquer vínculo, até que você se sinta confortável o suficiente com custos de implementação diferentes o suficiente para aumentar o design da plataforma e as preocupações gerais de design primeiro. Então, até então, aqui estão algumas maneiras que não lhe custarão em termos de complexidade (encargos de gerenciamento) ou de lidar com remoção ou alterações, se você mudar de idéia ...

Se o objetivo é medir e alocar, com a possibilidade de usar pools, é necessário pensar primeiro no conjunto mínimo de código habitável para começar. Por uma questão de explicação, se você é parcial em relação às classes, você pode criar uma classe e deixar que resista a uma pilha, ou então usar um conjunto de funções que usam um identificador ou um nome de pilha. É realmente uma questão de semântica para ser honesto. A próxima decisão é nova ou malloc; Sou parcial com malloc porque muitas vezes estou lidando com construções de baixo nível e sei na maioria das implementações que novas chamadas malloc, e não preciso me preocupar com a complexidade de sobrecarregar novas e com isso em todas as plataformas . No entanto, muitas vezes construí sistemas ou componentes para sobrecarregar ou conectar novos. E, claro, o principal problema ou diferença é que 'novo' deve saber o tipo antes da alocação, onde 'malloc' não se importa e com malloc você decide um tipo após a alocação. Todo esse detalhe é uma ideia e um contexto para a tomada de decisões de design nesses tipos é importante :)

Então, eu vou escolher classe e malloc, porque é mais fácil explicar aqui, mas isso realmente não importa no final. Os internos acabam tendo pouca diferença de material em comparação com o restante do projeto geral.

Portanto, nessa hipótese, eu sei que (ou vou assumir que) eu posso acabar com 7-8 instanciações de classe de subsistema real e antecipar centenas de milhares de chamadas para alocação e livre. Como grande parte da minha curiosidade e força real são tudo sobre o tamanho e o perfil, não quero sobrecarregar o desempenho do aplicativo. Para iniciantes, eu posso decidir deixar a coisa toda aberta e pública até que ela seja acertada, enquanto passo a implementá-la por todo o resto do aplicativo; uma estrutura fará isso muito bem. O 's_' é para mostrar quais vars são claramente destinados a estatísticas.

struct Mem
{
  int s_allocs;
  int s_frees;
  int s_peak;
  int s_current;
  void* heap; // if you wanted to go into having real actual separate heaps, else ignore
  void* alloc(int size);
  void free(void* p);

  Mem() {memset(this,0,szieof(Mem));}  // want this to be inlined with the call site constructor (a design decision example)
}

class MySubSystem
{
   Mem mem;
   ....  you get the idea
}

Isto é extremamenteleve em muitas frentes, e talvez um bom lugar para começar a se desenvolver onde quer que você realmente queira ir com isso. E você imediatamente tem um problema, como você sabe o tamanho do item liberado. (Esse seria um problema a ser resolvido para praticamente qualquer abordagem.) Como este é um fórum de jogos, você pode considerar dopar os primeiros bytes com o tamanho, ou então é necessário agrupar ou lembrar de alguma outra maneira. A maioria das sensibilidades dos desenvolvedores de jogos não deve ser muito contra o doping, e é o exemplo mais simples, considerando que eu já fiz uma parede de texto. Basicamente, é assim: você não quer se pode ser ajudado a destruir o alinhamento inerente, você quer saber, já que quase de graça, se o tamanho é coerente. Então, algo tão simples como "s_allocs ++; s_total + = tamanho; uint64 * p = (uint64 *) malloc / calloc (tamanho + = 8); * p = 0xDEADDAED00000000 | Tamanho; return p + 1; "onde as alocações serão inferiores a 4 GB e uint64 é o que o compilador achar que é um int não assinado de 64 bits e onde você pode verificar o valor da sanidade gratuitamente.

Essa é uma maneira de obter o mínimo necessário a um custo mínimo adequado que atenda aos requisitos. Ele faz nãoendereço que aloca classes que possuem funções virtuais, se houver espaço para criação de perfil ou gerenciamento, já que você não pode prever o tamanho do ambiente c ++ que você está usando precisa para aqueles sem sobrecarregar ou conectar novos, ou se estiver contando com o construtor em um dos maneiras estranhas que não poderiam ser tratadas por alguma outra função 'init'. Caso contrário, uma estrutura é uma classe, é uma alocação arbitrária e é a mesma quando você faz a conversão. Se você é parcial em relação a novas e precisa da semântica inerente de tabela ou construtor virtual, deve vincular uma nova, mas esse é um animal totalmente diferente, que você precisa realmente estudar para se certificar de que está fazendo as novas necessidades e precisa sinalizar seu código está lidando com novo, ao qual isso é aplicado. Mas, caso contrário, o conceito acima é o mesmo.

Mais importante, isso deve estimular o cérebro e, esperançosamente, o que você precisa e quais são suas tolerâncias, agora que você viu um pouco mais a cortina. Não há assistente :)

Celess
fonte
Se você estivesse usando funções de modelo para alocar e liberar, não seria um problema determinar o tamanho necessário para ser liberado (e alocado).
API-Beast
11
O objetivo era mostrar o problema subjacente, a fim de ajudar a fornecer uma base para escolher qualquer abstração, não apresentar uma abstração específica. Tudo tem suas vantagens e desvantagens. O conhecimento do tamanho é necessário, não para a ação de libertar, mas para as estatísticas.
Celess
1

Você não precisa implementar nada no seu jogo para esses dados. Ferramentas como o Massif Valgrind podem extrair todos os dados necessários dos símbolos de depuração. Você pode visualizar os despejos do Massif no Massif Visualizer .

API-Beast
fonte
4
Eu acho que a maioria dos jogos gráficos não será desenvolvida em um sistema que roda o Valgrind, infelizmente.
Kylotan
1

Eu recomendo não escrever seu próprio alocador de memória. Você precisa de um estável, confiável e testado, com boa funcionalidade de depuração, como detecção de corrupção e, o mais importante: com estatísticas confiáveis. Esta não é uma tarefa fácil e tem muitas armadilhas. Existem excelentes e fáceis de utilizar por aí, por exemplo:

Alocador de Doug Lea

Ele vem com o conceito de espaços de memória, você pode usar um por subsistema. É altamente otimizado e oferece ótimas estatísticas e informações de tempo de execução.

Maik Semder
fonte