ECS? In realtà suggerirei che potrebbe non essere prematuro, in questo caso, mettere un sacco di riflessioni sul lato orientato ai dati del design e confrontare diversi rappresentanti perché potrebbe avere un impatto sui progetti di interfacce e quest'ultimo è molto costoso cambiare in ritardo nel gioco. Inoltre ECS richiede un sacco di lavoro e di pensiero in anticipo e penso che valga la pena di utilizzare un po 'di quel tempo per assicurarsi che non ti darà più il lutto delle prestazioni a livello di progettazione, dato che sarà il cuore del tuo l'intero motore che fa impazzire Questa parte mi guarda fuori:
unordered_map<string,[yada]>
Anche con piccole ottimizzazioni delle stringhe, si ha un contenitore di dimensioni variabili (stringhe) all'interno di un altro contenitore di dimensioni variabili (unordered_maps). In effetti, le piccole ottimizzazioni delle stringhe potrebbero essere dannose in questo caso se la tabella è molto sparsa, poiché l'ottimizzazione della stringa piccola implicherebbe che ogni indice inutilizzato della tabella hash utilizzi ancora più memoria per l'ottimizzazione SS ( sizeof(string)
sarebbe molto più grande) al punto in cui il sovraccarico totale della memoria della tua tabella hash potrebbe costare più di qualunque cosa tu stia memorizzando, specialmente se si tratta di un componente semplice come un componente di posizione, oltre a incorrere in più errori di cache con l'enorme passo per passare da una voce nella tabella hash alla successiva.
Suppongo che la stringa sia una specie di chiave, come un ID componente. Se è così, questo rende già le cose molto più economiche:
unordered_map<int,[yada]>
... se desideri i vantaggi di poter disporre di nomi intuitivi che gli sceneggiatori possono utilizzare, ad es., le stringhe internate possono darti il meglio di entrambi i mondi qui.
Detto questo, se puoi mappare la stringa a un intervallo ragionevolmente basso di indici densamente usati, potresti essere in grado di farlo:
vector<[yada]> // the index and key become one and the same
Il motivo per cui non ritengo questo prematuro è perché, ancora una volta, potrebbe avere un impatto sul design dell'interfaccia. Il punto di DOD non dovrebbe essere quello di cercare di trovare le rappresentazioni dei dati più efficienti immaginabili in un colpo solo IMO (che dovrebbe essere generalmente realizzato in modo iterativo se necessario), ma pensandoci su abbastanza per progettare interfacce in alto per lavorare con quello dati che ti lasciano abbastanza spazio per profilare e ottimizzare senza cambiamenti di design a cascata.
Come esempio ingenuo, un software di elaborazione video che accoppia tutto il suo codice a questo:
// Abstract pixel that could be concretely represented by
// RGB, BGR, RGBA, BGRA, 1-bit channels, 8-bit channels,
// 16-bit channels, 32-bit channels, grayscale, monochrome,
// etc. pixels.
class IPixel
{
public:
virtual ~IPixel() {}
...
};
Non arriverà lontano senza una riscrittura potenzialmente epica, dal momento che l'idea di astrazione a livello di singolo pixel è già estremamente inefficiente (il vptr
di per sé spesso costerebbe più memoria del intero pixel) rispetto all'astrazione al livello immagine (che spesso rappresenta milioni di pixel). Quindi metti abbastanza in mente le tue rappresentazioni dei dati in modo da non dover affrontare uno scenario da incubo, e idealmente non di più, ma qui penso che valga la pena di pensare a questa roba in anticipo dato che non vuoi costruire un intricato motore attorno alla tua ECS e scopri che l'ECS stesso è il collo di bottiglia in modi che richiedono di cambiare le cose a livello di progettazione.
Per quanto riguarda le mancanze della cache ECS, a mio parere gli sviluppatori spesso cercano troppo difficile rendere la cache ECS amichevole. Inizia a cedere troppo poco al rischio di tentare di accedere a tutti i componenti in modo perfettamente contiguo, e spesso implica la copia e lo shuffling di dati dappertutto. Di solito è sufficiente per, ad esempio, radix gli indici dei componenti di ordinamento prima di accedervi, in modo che tu li stia accedendo in un modo in cui almeno non stai caricando un'area di memoria in una linea della cache, solo per espellerlo e quindi caricare tutto da capo nello stesso ciclo solo per accedere a una parte diversa della stessa linea della cache. E un ECS non deve fornire una straordinaria efficienza su tutta la linea. Non è come se un sistema di input ne tragga beneficio quanto un sistema di fisica o di rendering, quindi consiglio di puntare ad una "buona" efficienza su tutta la linea e "eccellente" solo nei luoghi in cui ne hai davvero bisogno. Detto questo, l'uso di unordered_map
e string
qui sono abbastanza facili da evitare.