Come eseguire il profiling e il pooling di memoria per sistema?


8

Sono stato interessante nel profilare e mantenere un pool di memoria gestita per ciascun sottosistema, quindi ho potuto ottenere statistiche sulla quantità di memoria utilizzata in qualcosa come suoni o grafica. Tuttavia, quale sarebbe un design che funziona per fare questo? Stavo pensando di utilizzare più allocatori e di utilizzarne uno solo per sottosistema, tuttavia, ciò avrebbe comportato variabili globali per i miei allocatori (o almeno così mi sembrerebbe). Un altro approccio che ho visto / suggerito è semplicemente sovraccaricare il nuovo e passare un allocatore per un parametro.

Ho avuto una domanda simile su StackOverflow qui con una generosità, tuttavia, sembra che forse fossi troppo vago o semplicemente non ci siano abbastanza persone con conoscenza in materia.


Rimossa la mia risposta Sono tornato indietro e ho letto gli articoli e i pool utilizzati per tenere traccia delle allocazioni per sistema non fanno parte di quegli articoli (anche se erano basati su di esso) Se riesco a trovare di nuovo quelli specifici, li collegherò invece! Scusate!
James,

1
Dai un'occhiata a questo post sul blog di Jesus de Santos Garcia. In esso, discute il monitoraggio della memoria per sottosistema e l'utilizzo di più allocatori per vari requisiti di memoria.

7
Non essere ossessionato dai paradigmi teorici. Se hai bisogno di funzionalità e dati a livello globale, non c'è nulla di sbagliato nei globuli. Esistono già un sacco di funzioni globali come new / delete / malloc / free. Aggiungi semplicemente quello che devi fare per completare il lavoro.
Maik Semder

Risposte:


1

È sicuramente una domanda che potrebbe sembrare vaga ad alcuni;)

Ma penso di sapere da dove vieni.

Hai un milione di scelte su come potresti scegliere di implementarlo. Alcune di queste scelte dovrebbero riguardare sia le piattaforme target sia gli obiettivi generali di progettazione. Queste considerazioni spezzeranno ogni legame, fino a quando non ti sentirai abbastanza a tuo agio con costi di implementazione diversi abbastanza da far crescere prima il design dalla piattaforma e le preoccupazioni generali di design. Quindi fino ad allora, ecco alcuni modi che non ti costeranno in termini di complessità (onere di gestione) o di gestione della rimozione o dei cambiamenti se cambi idea ...

Se l'obiettivo è misurare e allocare, con la possibilità di utilizzare i pool, è necessario innanzitutto pensare al set minimo di codice vivibile per iniziare. Per motivi di spiegazione, se sei parziale alle classi, puoi creare una classe, lasciare che rappresenti un heap o utilizzare invece un set di funzioni che prendono un handle o un nome di heap. È davvero un problema di semantica per essere onesti. La prossima decisione è nuova o malloc; Sono parziale di malloc perché molte volte ho a che fare con costrutti di basso livello e so nella maggior parte delle implementazioni che i nuovi chiamano malloc e non devo preoccuparmi della complessità del sovraccarico di nuovi e di preoccuparmi di ciò su tutte le piattaforme . Tuttavia, molte volte ho costruito sistemi o componenti per sovraccaricare o collegare nuovi. E ovviamente il problema principale o la differenza, è che "nuovo" deve conoscere il tipo prima dell'allocazione, dove a "malloc" non importa e con malloc si risolve in un tipo dopo l'allocazione. Tutto quel dettaglio è darti un'idea e un po 'di contesto per prendere decisioni di progettazione in questi tipi è importante :)

Quindi sceglierò classe e malloc, perché è più facile da spiegare qui, ma alla fine non importa. Gli interni finiranno per portare poca differenza di materiale rispetto al resto del design complessivo.

Quindi, in questo ipotetico, so che (o assumerò che) potrei finire con 7-8 istanze di classe di sottosistemi reali e anticipare centinaia di migliaia di chiamate per allocazione e libero. Dato che gran parte della mia curiosità e della mia vera spinta in tutto questo, riguarda davvero le dimensioni e il profilo, non voglio appesantire le prestazioni dell'app. Per i principianti potrei decidere di lasciare tutto aperto e pubblico fino a quando non lo avrò inchiodato, mentre passo attraverso l'implementazione in tutto il resto dell'app; una struttura lo farà bene. 'S_' indica quali variabili sono chiaramente destinate alla statistica.

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
}

Questo è estremamenteleggero su molti fronti, e forse un buon posto per iniziare a perfezionare dove mai davvero hai voluto andare con questo. E hai subito un problema, come fai a sapere le dimensioni dell'articolo liberato. (Questo sarebbe un problema da risolvere per quasi tutti gli approcci.) Dato che si tratta di un forum di gioco, potresti prendere in considerazione il doping dei primi byte con le dimensioni, oppure devi avvolgere o ricordare in qualche altro modo. La maggior parte della sensibilità degli sviluppatori di giochi non dovrebbe essere troppo contro il doping, ed è l'esempio più semplice, considerando che ho già creato un muro di testo. Fondamentalmente va così: non vuoi che possa essere aiutato a distruggere l'allineamento intrinseco, vuoi sapere, quasi gratis, se la dimensione è coerente. Quindi qualcosa di semplice come "s_allocs ++; s_total + = size; uint64 * p = (uint64 *) malloc / calloc (size + = 8); * p = 0xDEADDAED00000000 | dimensione; return p + 1; "dove le allocazioni saranno inferiori a 4 GB e uint64 è qualunque cosa il compilatore ritenga che sia un int senza segno a 64 bit e dove è possibile controllare gratuitamente il valore di integrità.

Questo è tutto un modo per ottenere il minimo indispensabile a un minimo costo che soddisfa i requisiti. Lo fa Nonindirizzare le classi di allocazione che hanno funzioni virtuali se sono nell'ambito della profilazione o della gestione, poiché non si può prevedere la dimensione dell'ambiente c ++ che si sta utilizzando per quelli senza sovraccarico o collegamento di nuovi, oppure se si fa affidamento sul costruttore in uno dei modi strani che non potevano essere gestiti da un'altra funzione "init". Altrimenti uno stuct è una classe è un'allocazione arbitraria ed è lo stesso quando lanci. Se sei parziale con il nuovo e hai bisogno della tabella virtuale intrinseca o della semantica del costruttore, allora devi agganciarne di nuovi, ma quello è un altro "altro animale", che devi davvero studiare per assicurarti di fare ciò che le nuove esigenze e dovrebbero segnalare il tuo codice è nuovo, a cui si applica questo bucket. Ma per il resto il concetto sopra è lo stesso.

Ancora più importante, questo dovrebbe far andare il tuo cervello, e si spera sulla falsariga di ciò di cui hai bisogno e quali sono le tue tolleranze, ora che hai visto un po 'di più dietro il sipario. Non esiste un mago :)


Se si utilizzavano le funzioni modello per l'allocazione e la liberazione, non dovrebbe essere un problema determinare la dimensione necessaria per essere liberata (e allocata).
API-Beast

1
Il punto era mostrare il problema sottostante per aiutare a fornire una base per scegliere qualunque astrazione, non per presentare una particolare astrazione. Tutto ha i suoi compromessi. La conoscenza delle dimensioni è necessaria non per fare l'atto di liberazione, ma per le statistiche.
Celess,

1

Non è necessario implementare nulla nel gioco per questi dati. Strumenti come Massif Valgrind possono estrarre tutti i dati necessari dai simboli di debug. È possibile visualizzare i dump di Massif in Massif Visualizer .


4
Immagino che la maggior parte dei giochi grafici non saranno sviluppati su un sistema che esegue Valgrind, sfortunatamente.
Kylotan,

1

Consiglio vivamente di non scrivere il proprio allocatore di memoria. Ne hai bisogno di uno stabile, affidabile e testato, con buone funzionalità di debug come il rilevamento della corruzione e, soprattutto, con statistiche affidabili. Questo non è un compito facile e presenta molte insidie. Ci sono fantastici e facili da usare quelli là fuori, ad esempio:

Doug Lea Allocator

Viene fornito con il concetto di spazi di memoria, è possibile utilizzarne uno per sottosistema. È altamente ottimizzato e ti dà grandi statistiche e informazioni di runtime.

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.