2010-10-08 12 views
5

La mia domanda è basata sulla curiosità e non sul fatto che ci sia un altro approccio al problema o meno. È una domanda strana/interessante, quindi ti preghiamo di leggerla con una mente aperta.Iterare senza incorrere nel costo delle dichiarazioni IF

Supponiamo che ci sia un ciclo di gioco chiamato ogni frame. Il ciclo di gioco a sua volta chiama diverse funzioni attraverso una miriade di istruzioni if. Ad esempio, se l'utente ha una GUI su false, non aggiornare la GUI, altrimenti chiama RefreshGui(). Ci sono molte altre istruzioni if nel ciclo e chiamano le loro rispettive funzioni se sono vere. Alcuni sono if/if-else.../else che sono più costosi nel peggiore dei casi. Anche le funzioni chiamate, se l'istruzione if è vera, hanno una logica. Se l'utente desidera il raypicking su tutti gli oggetti chiama FunctionA(), se l'utente desidera raypicking sulle luci, chiama FunctionB(), ..., altrimenti chiama tutte le funzioni. Spero che tu abbia l'idea.

Il mio punto è che si tratta di molte affermazioni ridondanti. Così ho deciso di usare invece i puntatori di funzione. Ora la mia ipotesi è che un puntatore a funzione sarà sempre più veloce di una dichiarazione if. È un sostituto per se/else. Quindi, se l'utente desidera passare tra due diverse modalità di fotocamera, lui/lei preme la chiave C per alternare tra loro. La funzione di callback per la tastiera cambia il puntatore alla funzione UpdateCamera corretta (in questo caso, il puntatore della funzione può puntare a UpdateCameraFps() o UpdateCameraArcBall()) ... ne viene il senso.

Ora alla domanda stessa. Cosa succede se ho diverse funzioni di aggiornamento tutte con la stessa firma (diciamo annullamento (*Update)(float time)), in modo che un puntatore a funzione possa potenzialmente puntare a una di esse. Quindi, ho un vettore che viene utilizzato per memorizzare i puntatori. Poi nel mio ciclo di aggiornamento principale, vado attraverso il vettore e chiamo ogni funzione di aggiornamento. Posso rimuovere/aggiungere e persino modificare l'ordine degli aggiornamenti, senza modificare il codice sottostante. Nel migliore dei casi, potrei chiamare solo una funzione di aggiornamento o, nel peggiore dei casi, tutte, tutte con un ciclo while molto pulito e nessuna istruzione negativa (potenzialmente annidata). Ho implementato questa parte e funziona benissimo. Sono consapevole del fatto che, ad ogni iterazione del ciclo while responsabile per l'iterazione attraverso il vettore, sto verificando se lo itrBegin == itrEnd. Più precisamente while (itrBegin != itrEnd). C'è un modo per evitare la chiamata alle istruzioni if? Posso usare la previsione del ramo a mio vantaggio (o ne approfitto già senza saperlo)?

Ancora una volta, si prega di prendere la domanda così com'è, io non sto cercando un approccio diverso (anche se sei più che benvenuto a dare uno).

EDIT: Alcuni risposte affermano che si tratta di un'ottimizzazione prematura non necessario e non dovrebbe concentrarsi su di esso e che il costo se-statement (s) è minuscola rispetto al lavoro svolto in tutte le funzioni di aggiornamento separati . Verissimo, e sono completamente d'accordo, ma non era questo il punto della domanda e mi scuso se non ho chiarito la questione. Ho imparato un bel po 'di cose nuove con tutte le risposte però!

+5

Se ognuna di queste funzioni sta eseguendo un'attività "grande" (come l'aggiornamento della GUI), sicuramente il costo di un singolo confronto è assolutamente irrilevante al confronto? Hai effettivamente profilato per vedere quale percentuale del tempo speso nel tuo ciclo di driver? Scommetto che questa "ottimizzazione" non vale il costo dell'offuscamento del codice. –

+1

* [...] quindi per favore leggilo con una mente aperta "* Questo tipo di affermazione mi rende * tanto * molto più probabile leggere ciò che segue con attenzione a come potrebbe essere legittimamente chiuso. Ma ... um .. .se stai utilizzando un linguaggio polimorfo, ad esempio C++, perché non usi questa caratteristica del linguaggio per risolvere questo problema? È un classico caso d'uso per mostrare come il codice OOP può essere più pulito di quanto non lo sia codice. :: sheesh :: – dmckee

+1

+1 Interessante domanda con buone intenzioni. –

risposta

6

c'è un ciclo di gioco che viene chiamato ogni fotogramma

Questo è un modo a ritroso di descriverlo.Un ciclo di gioco non viene eseguito durante un frame, una cornice viene gestita nel corpo del ciclo di gioco.

la mia ipotesi è che un puntatore a funzione sta andando sempre essere più veloce di un'istruzione if

Avete provato questo? Non è probabile che sia vero, specialmente se stai cambiando frequentemente il puntatore (che in realtà mette a rischio la previsione della branch della CPU).

Posso utilizzare la previsione del ramo a mio vantaggio (o sto già sfruttando il vantaggio senza saperlo)?

Questo è solo un pio desiderio. Avendo una chiamata indiretta all'interno del ciclo che chiama un gruppo di funzioni diverse, si sta sicuramente lavorando contro la logica di previsione dei rami della CPU.

Più specificatamente mentre (itrBegin! = ItrEnd). C'è un modo per evitare la chiamata alle istruzioni if?

Una cosa che si può fare per evitare condizionali mentre si itera la catena di funzioni è usare un elenco collegato. Quindi ciascuna funzione può chiamare il successivo incondizionatamente e si installa semplicemente la logica di terminazione come ultima funzione della catena (longjmp o qualcosa). Oppure potresti semplicemente non terminare mai, includere glSwapBuffers (o l'equivalente per la tua API grafica) nell'elenco e collegarlo all'inizio.

+0

Questa è una bella idea. +1 – JoshD

+0

@JoshD: Hai appena ricevuto un altro voto, vero? –

+2

E non solo previsione delle filiali! L'uso dei puntatori di funzione impedirà al compilatore di inlining, il che potrebbe ridurre drasticamente le opportunità di ottimizzazione - il sovraccarico di una chiamata di funzione (che evita di inlining) è più volte quella di una semplice istruzione if. Se il codice è in linea e nella cache delle istruzioni, potrebbe essere _sumer centinaia di volte più veloce della chiamata del puntatore alla funzione! – Gretchen

0

Se le condizioni controllate dai if non cambiano durante il ciclo, è possibile controllarle tutte una volta e impostare un puntatore a funzione per la funzione che si desidera chiamare in quel caso. Quindi nel ciclo chiamate la funzione a cui punta il puntatore della funzione.

1

Per me, questo è chiedere un approccio guidato dagli eventi. Invece di controllare ogni volta se devi fare qualcosa, controlla che la richiesta in entrata faccia qualcosa.

Non so se si considera che una deviazione dal vostro approccio, ma sarebbe ridurre il numero di IF ... THEN a 1.

while(active) 
{ 
    // check message queue 
    if(messages) 
    { 
     // act on each message and update flags accordingly 
    } 

    // draw based on flags (whether or not they changed is irrelevant) 
} 

EDIT: Anche io sono d'accordo con il poster chi ha affermato che il ciclo non dovrebbe essere basato sui frame; i fotogrammi dovrebbero essere basati sul ciclo.

4

Innanzitutto, profila il tuo codice. Quindi ottimizza le parti che ne hanno bisogno.

"se" le affermazioni sono l'ultimo dei tuoi dubbi. In genere, con l'ottimizzazione, ci si concentra su loop, operazioni di I/O, chiamate API (ad esempio SQL), contenitori/algoritmi che sono inefficienti e utilizzati di frequente.

Utilizzare i puntatori di funzione per cercare di ottimizzare è in genere la cosa peggiore che si possa fare. Uccidi ogni possibilità di leggibilità del codice e lavori contro la CPU e il compilatore. Raccomando di usare il polimorfismo o semplicemente usare le istruzioni "se".

+0

+1 per il profilo del codice –

+3

Il polimorfismo è solo uno strato aggiuntivo di riferimento indiretto (probabilmente con una località orribile) sopra un puntatore a funzione. –

+0

I compilatori sanno come trattare il polimorfismo, ma i puntatori di funzione sono puro caos dal punto di vista dell'ottimizzazione. Il polimorfismo dice "otterrai un oggetto come questo" mentre i puntatori alle funzioni dicono "Sto mischiando il mazzo e ti distribuisco una carta e ti piacerà" –

Problemi correlati