Quando sto codifica a motore, sono spesso preoccupati solo con un fisso n
: Ho già una partizione spaziale che limita il numero di oggetti che riceve update()
, physics()
erender()
a circa quelli su schermo e le zone circostanti. La dimensione massima del batch è di solito abbastanza ben definita per gioco, sebbene invariabilmente sia un po 'più grande di quanto tu abbia pianificato.
In questo caso non mi occupo tanto del big-O quanto del moltiplicatore del fattore costante e dei termini di ordine inferiore. Per una funzione con runtime come a*n^2 + b*n + c
(che è O(n^2)
), spesso mi preoccupo molto di ridurre a
e possibilmente eliminare c
. Un costo di installazione o smontaggio c
può diventare proporzionalmente grande rispetto a un piccolo n
.
Tuttavia, questo non vuol dire che big-O (o più particolarmente big-theta ) sia un ottimo indicatore di odore di codice. Vedere un O(n^4)
da qualche parte, o peggio ancora unO(k^n)
tempo geometrico, ed è tempo di assicurarti di prendere in considerazione altre opzioni.
In genere sono molto più preoccupato per l'ottimalità del big-O e per saltare attraverso i cerchi per trovare algoritmi con big-O inferiore quando ho a che fare con strumenti per la creazione di dati. Mentre il numero di oggetti in un determinato livello / area di streaming è generalmente ben definito, il numero totale di oggetti / risorse artistiche / file di configurazione / ecc in un intero gioco potrebbe non esserlo. È anche un numero molto più grande. Anche eseguendo una creazione di dati paralleli, aspettiamo ancora nell'ordine di un minuto (lo so, piagnucolare - i dati per console possono richiedere ore - siamo per lo più piccoli giochi portatili) per passare attraverso un jam data-clean && jam data
ciclo.
Per fare un esempio specifico: questo è stato davvero fuori controllo con un algoritmo di streaming di piastrelle di sfondo che riproduce le tessere 8x8 256 colori. È utile condividere i buffer di streaming tra "livelli" di sfondo e potremmo averne fino a 6 in un dato livello condividendo lo stesso buffer. Il problema è che la stima della dimensione del buffer necessaria si basa sulle possibili posizioni di tutti e 6 i livelli - e se si tratta di una larghezza / altezza / velocità di scorrimento del numero primo, si inizia rapidamente ad iniziare una ricerca esaustiva - che inizia ad avvicinarsiO(6^numTiles)
- che è in molti casi nella categoria "più lungo dell'universo sarà intorno". Fortunatamente la maggior parte dei casi ha solo 2-3 livelli, ma anche in questo caso, siamo in grado di superare la mezz'ora di autonomia. Al momento, campioniamo un piccolo sottoinsieme di queste possibilità, aumentando la granularità fino a quando non è trascorso un determinato periodo di tempo (o abbiamo completato l'attività, che può accadere per piccole configurazioni a doppio strato). Aumentiamo un po 'questa stima in base alle statistiche precedenti sulla frequenza con cui ci siamo dimostrati sbagliati, quindi aggiungiamo un po' di riempimento extra per una buona misura.
Un altro esempio divertente: su un gioco per PC un po 'di tempo fa, l'ingegnere capo ha sperimentato per un po' con skip list . L'overhead di memoria finisce per causare più effetti cache, il che aggiunge una sorta di moltiplicatore non costante all'intera faccenda - quindi non sono affatto una buona scelta per i piccoli n
. Ma per elenchi più grandi in cui le ricerche sono frequenti, offrono un vantaggio.
(Trovo spesso che l'algoritmo ingenuo sia big-O superiore, più veloce su insiemi di dati più piccoli e più facile da capire; quelli più interessanti / complessi (ad esempio patricia trie) sono più difficili da comprendere e mantenere per le persone, ma prestazioni più elevate su più grandi set di dati.)