Sto ottimizzando prematuramente?

9

Sono attualmente in fase di progettazione di un'architettura basata su componenti in C ++.

Il mio attuale design include l'uso di funzioni come:

  • std::vector s di std::shared_ptr s per contenere i componenti
  • std::dynamic_pointer_cast
  • std::unordered_map<std::string,[yada]>

I componenti rappresenteranno i dati e la logica dei vari elementi necessari in un software di gioco, come grafica, fisica, intelligenza artificiale, audio, ecc.

Ho letto da un capo all'altro che le cache mancano di prestazioni, quindi ho eseguito alcuni test, il che mi ha portato a credere che, in effetti, possa rallentare un'applicazione.

Non sono stato in grado di testare le funzionalità linguistiche sopramenzionate, ma in molti punti si dice che queste tendono a costare molto e dovrebbero essere evitate se possibile.

Dato che sono nella fase di progettazione dell'architettura, e questi saranno inclusi nel nucleo del design, dovrei cercare di trovare modi per evitarli ora, dato che sarà molto difficile cambiarlo in un secondo momento se ci sono problemi di prestazioni?

O sono solo preso dall'ottimizzazione prematura?

    
posta Alexandre Vaillancourt 26.07.2016 - 23:11
fonte

4 risposte

26

Senza leggere altro che il titolo: Sì.

Dopo aver letto il testo: Sì. Anche se è vero che mappe e puntatori condivisi ecc. Non funzionano bene in termini di cache, sicuramente troverai che quello che vuoi usarli - per quanto ho capito - non è il collo di bottiglia e non verrà trattenuto o utilizzare la cache in modo efficiente a prescindere dalla struttura dei dati.

Scrivi il software evitando gli errori più stupidi, quindi prova, poi trova i colli di bottiglia, quindi ottimizza!

Fwiw: link

    
risposta data 26.07.2016 - 23:24
fonte
3

Non ho familiarità con C ++, ma in generale dipende.

Non è necessario ottimizzare prematuramente gli algoritmi isolati in cui è possibile ottimizzare facilmente quando si arriva a questo.

Tuttavia è necessario ottenere una progettazione generale dell'applicazione per raggiungere gli indicatori di prestazioni chiave desiderati.

Ad esempio, se è necessario progettare un'applicazione per servire milioni di richieste al secondo, è necessario pensare alla scalabilità dell'applicazione durante la progettazione, piuttosto che far funzionare l'applicazione.

    
risposta data 27.07.2016 - 02:12
fonte
3

Se devi chiedere, allora si. Ottimizzazione prematura significa ottimizzazione prima che tu sia sicuro c'è un significativo problema di prestazioni.

    
risposta data 16.08.2016 - 12:53
fonte
1

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.

    
risposta data 13.12.2017 - 06:41
fonte

Leggi altre domande sui tag