Perché è così male ottimizzare troppo presto?


168

Dopo aver esaminato un po 'l'ottimizzazione, ho scoperto (letteralmente ovunque) che sembra essere un peccato universalmente riconosciuto per ottimizzare un gioco troppo presto.

Davvero non capisco questo, non sarebbe incredibilmente difficile cambiare alcune delle strutture principali del gioco alla fine, piuttosto che svilupparle la prima volta pensando alle prestazioni?

Aspetto che aspettare che il gioco sia finito ti dirà se hai anche bisogno di ottimizzazioni, ma non dovresti farlo comunque, dopo tutto, potrebbe ampliare la varietà di dispositivi su cui il gioco potrebbe funzionare, il che aumenterebbe il numero di potenziali Giocatori.

Qualcuno potrebbe spiegarmi perché è una cattiva idea ottimizzare troppo presto?


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Josh

3
Non potrebbe essere più semplice rispondere a questa domanda: "fa perdere tempo". Potresti anche chiedere "perché cercare di risparmiare denaro durante lo shopping?"
Fattie,

27
Al contrario, nota che molti ingegneri affermano semplicemente che la frase "non ottimizzare troppo presto" è semplicemente sbagliata . La frase dovrebbe essere semplicemente "non ottimizzare fino a quando non sai cosa ottimizzare". Quindi, molto semplicemente, se ci sono tre sistemi A, B e C, è assolutamente inutile ottimizzare A se, si scopre, A non è in alcun modo un collo di bottiglia, e C è in realtà il collo di bottiglia. In quell'esempio, ovviamente, l'ottimizzazione di A sarebbe stata una totale perdita di tempo. Quindi - abbastanza semplicemente - non ottimizzare fino a quando non sai quale di A, BC ottimizzare: questo è tutto ciò che significa, è così semplice.
Fattie,

2
L'ottimizzazione della classe temporale dell'applicazione durante lo sviluppo è molto diversa rispetto al tentativo di radere alcune istruzioni in un ciclo che di solito è un tempo costante. Concentrati su O (n) vs O (n log n) invece che "scrivere un ciclo for in questo modo salva un'istruzione CPU".

1
@phyrfox In realtà, ridurre il tempo di caricamento mi sembra più impressionante. Si nota un tempo di caricamento di tre secondi. Non sono sicuro se si notino 55fps a 60fps.
immibis,

Risposte:


237

Preambolo:

Nei commenti sono state sollevate alcune obiezioni, e penso che derivino in gran parte da un fraintendimento di ciò che intendiamo quando diciamo "ottimizzazione prematura" - quindi volevo aggiungere un piccolo chiarimento al riguardo.

"Non ottimizzare prematuramente" non significa "scrivere codice che sai essere dannoso, perché Knuth dice che non ti è permesso ripulirlo fino alla fine"

Significa "non sacrificare tempo e leggibilità per l'ottimizzazione fino a quando non sai quali parti del tuo programma hanno effettivamente bisogno di aiuto per essere più veloci". Poiché un programma tipico trascorre la maggior parte del tempo in alcuni colli di bottiglia, investire nell'ottimizzazione di "tutto" potrebbe non ottenere lo stesso incremento di velocità del focalizzare lo stesso investimento solo sul codice del collo di bottiglia.

Ciò significa che, in caso di dubbio , dovremmo:

  • Preferisci un codice semplice da scrivere, chiaro da capire e facile da modificare per i principianti

  • Verifica se è necessaria un'ulteriore ottimizzazione (di solito eseguendo la profilazione del programma in esecuzione, anche se un commento sotto le note fa le analisi matematiche - l'unico rischio è che devi anche verificare che la tua matematica sia giusta)

Un'ottimizzazione prematura non è :

  • Decisioni architettoniche per strutturare il codice in modo tale da adattarsi alle proprie esigenze, scegliendo moduli / responsabilità / interfacce / sistemi di comunicazione appropriati in modo ponderato.

  • Semplici efficienze che non richiedono più tempo o rendono più difficile la lettura del codice. Cose come l'uso della digitazione forte possono essere sia efficienti che chiari le tue intenzioni. La memorizzazione nella cache di un riferimento invece di cercarlo ripetutamente è un altro esempio (a patto che il tuo caso non richieda una complessa logica di invalidazione della cache - forse tieni duro a scriverlo fino a quando non avrai profilato nel modo semplice prima).

  • Utilizzando l'algoritmo giusto per il lavoro. A * è più ottimale e più complesso della ricerca esaustiva di un grafico di pathfinding. È anche uno standard del settore. Ripetere il tema, attenersi a metodi collaudati come questo può effettivamente rendere il codice più facile da capire rispetto a se si fa qualcosa di semplice ma in contrasto con le migliori pratiche conosciute. Se hai esperienza con i colli di bottiglia nell'implementazione della funzione di gioco X in un modo su un progetto precedente, non è necessario ripetere nuovamente lo stesso collo di bottiglia su questo progetto per sapere che è reale: puoi e dovresti riutilizzare le soluzioni che hanno funzionato in passato Giochi.

Tutti questi tipi di ottimizzazioni sono ben giustificati e generalmente non sarebbero etichettati "prematuri" (a meno che non si stia scendendo in una tana di coniglio implementando un tracciato all'avanguardia per la mappa della scacchiera 8x8 ...)

Quindi ora con quello chiarito, sul perché potremmo trovare questa politica utile nei giochi in particolare:


Soprattutto in gamedev, la velocità di iterazione è la cosa più preziosa. Spesso implementeremo e reimplementeremo molte più idee di quelle che alla fine verranno fornite con il gioco finito, cercando di "trovare il divertimento".

Se riesci a prototipare un meccanico in un modo semplice e forse un po 'ingenuo e testarlo il giorno successivo, sei in una posizione molto migliore rispetto a se trascorri una settimana a crearne prima la versione più ottimale. Soprattutto se si scopre che fai schifo e finisci per lanciare quella funzione. Farlo in modo semplice in modo da poter eseguire i test in anticipo può risparmiare un sacco di lavoro sprecato ottimizzando il codice che non conservi.

Il codice non ottimizzato è anche generalmente più facile da modificare e provare diverse varianti rispetto al codice che è finemente ottimizzato per fare una cosa precisa in modo ottimale, che tende a essere fragile e più difficile da modificare senza romperlo, introdurre bug o rallentarlo. Pertanto, mantenere il codice semplice e facile da modificare spesso vale una piccola inefficienza di runtime durante gran parte dello sviluppo (di solito stiamo sviluppando su macchine al di sopra delle specifiche di destinazione, in modo da poter assorbire l'overhead e concentrarci sull'esperienza di destinazione prima) fino a quando abbiamo bloccato ciò di cui abbiamo bisogno dalla funzione e possiamo ottimizzare le parti che ora sappiamo essere lente.

Sì, il refactoring di parti del progetto in fase avanzata di sviluppo per ottimizzare i punti lenti può essere difficile. Ma lo è anche il refactoring ripetutamente durante lo sviluppo perché le ottimizzazioni apportate il mese scorso non sono compatibili con la direzione in cui il gioco si è evoluto da allora, o stavano risolvendo qualcosa che si è rivelato non essere il vero collo di bottiglia una volta ottenute più funzionalità e contenuti nel.

I giochi sono strani e sperimentali: è difficile prevedere come si evolverà un progetto di gioco e le sue esigenze tecnologiche e dove le prestazioni saranno ridotte. In pratica, finiamo spesso per preoccuparci delle cose sbagliate: cerca tra le domande sulla performance qui e vedrai emergere un tema comune di sviluppatori che vengono distratti da cose su carta che probabilmente non è affatto un problema.

Per fare un esempio drammatico: se il tuo gioco è associato alla GPU (non insolito), tutto quel tempo speso per l'ottimizzazione e il threading del lavoro della CPU potrebbe non produrre alcun beneficio tangibile. Invece, tutte quelle ore di sviluppo avrebbero potuto essere impiegate per implementare e perfezionare le funzionalità di gioco, per una migliore esperienza del giocatore.

Nel complesso, la maggior parte del tempo che passi a lavorare su un gioco non sarà speso per il codice che finisce per essere il collo di bottiglia. Soprattutto quando si lavora su un motore esistente, le cose super costose del circuito interno nei sistemi di rendering e fisica sono in gran parte fuori dalle tue mani. A quel punto, il tuo compito negli script di gioco è sostanzialmente quello di stare alla larga dal motore - fintanto che non ci metti una chiave lì, probabilmente uscirai abbastanza bene per una prima build.

Quindi, a parte un po 'di igiene del codice e budget (ad es. Non cercare / costruire ripetutamente roba se puoi riutilizzarla facilmente, mantieni modeste le tue ricerche di pathfinding / fisica o i readback della GPU, ecc.), Prendendo l'abitudine di non esagerare -ottimizzare prima di sapere dove sono i veri problemi si rivela positivo per la produttività - salvarci dal perdere tempo a ottimizzare le cose sbagliate e mantenere il nostro codice più semplice e facile da modificare nel complesso.


38
C'è qualche ulteriore discussione su StackOverflow qui , sottolineando l'importanza della leggibilità e della chiarezza nel codice e il pericolo di rendere il codice più difficile da comprendere (e più facile introdurre bug) ottimizzandolo prematuramente.
DMGregory

8
Ottima risposta, vorrei aggiungere, in risposta a " non sarebbe incredibilmente difficile cambiare alcune delle strutture principali del gioco alla fine, piuttosto che svilupparle la prima volta pensando alle prestazioni? " Direttamente, che ovviamente cerchi di progettare per l'efficienza in primo luogo. Quando ti vengono presentate due opzioni, scegli quella più efficiente. In caso contrario, scrivi il gioco nel modo più modulare possibile con interfacce ben definite. È quindi possibile modificare i meccanismi all'interno di ciascun modulo senza ristrutturare l'intera base di codice.
Baldrickk,

1
@Baldrickk Direi che vale la pena condividere come risposta aggiuntiva. (L'avrei votato!) La mia risposta salta sull'importanza dell'architettura e della pianificazione per evitare di creare grossi problemi in primo luogo. Per quanto riguarda il riferimento di Gizmo allo "sviluppo di programmi in generale", ecco da dove viene questo concetto di ottimizzazione prematura. Come indicato sopra, l'ottimizzazione errata può diventare un debito tecnico tanto facilmente quanto un'ottimizzazione mancante. Ma dovremmo sottolineare che non stiamo mai facendo le cose in modo deliberatamente lento, preferendo semplicemente la semplicità e la leggibilità fino a quando non saremo in grado di profilare e trovare i veri problemi
DMGregory

1
Anche se probabilmente sono più inesperto qui fuori di voi gente, devo dire che trovo questa "radice di ottimizzazione prematura di tutti i mali" un po 'strana. Due anni fa stavo provando a fare qualcosa in Unity3D. Ho scritto alcune query LINQ, ognuna usando la precedente. Ho guardato il mio codice e ho iniziato a nutrire forti dubbi. Pensavo che la complessità del calcolo fosse orribile. Ho preso un foglio di carta e ho calcolato che l'elaborazione di queste query su una dimensione prevista di dati avrebbe richiesto ore. Quindi ho aggiunto un po 'di cache qui e là. E le query stavano andando bene.
Gaazkam,

3
Quindi hai fatto la dovuta diligenza e verificato che il problema era reale prima di cambiare il codice da ciò che inizialmente era più chiaro e più intuitivo per te. Ciò significa che la tua ottimizzazione non è stata prematura. ;) "Evitare l'ottimizzazione prematura anticipata" non significa "scrivere codice inutilmente costoso" - solo per favorire semplicità, leggibilità, facilità di modifica fino a quando non viene identificato un problema prestazionale definito. I commenti non sono destinati alla discussione, quindi possiamo passare alla chat se desideri parlarne ulteriormente.
DMGregory

42

nota: questa risposta è iniziata come un commento sulla risposta di DMGregory, e quindi non duplica gli ottimi punti che fa.

"Non sarebbe incredibilmente difficile cambiare alcune delle strutture principali del gioco alla fine, piuttosto che svilupparle la prima volta pensando alle prestazioni?"

Questo, per me, è il nocciolo della domanda.

Quando crei il tuo design originale, dovresti provare a progettare per l'efficienza, al massimo livello. Questa è meno ottimizzazione e riguarda più la struttura.

Esempio:
è necessario creare un sistema per attraversare un fiume. I disegni ovvi sono un ponte o un traghetto, quindi quale scegli?
La risposta ovviamente dipende dalle dimensioni della traversata e dal volume del traffico. Questa non è un'ottimizzazione, ma inizia invece con un design adatto al tuo problema.

Quando ti vengono presentate scelte di design, scegli quella più adatta a ciò che vuoi fare.

Quindi, supponiamo che il nostro volume di traffico sia piuttosto basso, quindi decidiamo di costruire due terminali e acquistare un traghetto per gestire il traffico. Una semplice implementazione semplice
Sfortunatamente, una volta installato e funzionante, scopriamo che sta vedendo più traffico del previsto. Dobbiamo ottimizzare il traghetto! (perché funziona e costruire un ponte ora non è un buon piano)

Opzioni:

  • Acquista un secondo traghetto (elaborazione parallela)
  • Aggiungi un altro ponte auto al traghetto (compressione del traffico)
  • Aggiorna il motore del traghetto per renderlo più veloce (algoritmi di elaborazione riscritti)

Qui è dove dovresti cercare di rendere il tuo design originale il più modulare possibile.
Tutti i precedenti sono possibili ottimizzazioni e potresti persino fare tutti e tre.
Ma come si apportano questi cambiamenti senza grandi cambiamenti strutturali?

Se hai un design modulare con interfacce chiaramente definite, dovrebbe essere semplice implementare queste modifiche.
Se il codice non è strettamente accoppiato, le modifiche ai moduli non influiscono sulla struttura circostante.

Diamo un'occhiata all'aggiunta di un traghetto extra.
Un programma "cattivo" potrebbe essere costruito attorno all'idea di un singolo traghetto e avere gli stati del molo e lo stato del traghetto e posizionare tutti raggruppati insieme e condividendo lo stato. Questo sarà difficile da modificare per consentire l'aggiunta di un ulteriore traghetto al sistema.
Un progetto migliore sarebbe quello di avere le banchine e il traghetto come entità separate. Non c'è alcun accoppiamento stretto tra loro, ma hanno un'interfaccia, in cui un traghetto può arrivare, scaricare passeggeri, prenderne di nuovi e partire. Il dock e il traghetto condividono solo questa interfaccia e ciò semplifica le modifiche al sistema, in questo caso aggiungendo un secondo traghetto. Al dock non importa cosa siano effettivamente i traghetti, tutto ciò che riguarda è che qualcosa (qualsiasi cosa) stia usando la sua interfaccia.

tl; dr:

  • Prova a progettare per l'efficienza in primo luogo.
  • Quando ti vengono presentate due opzioni, scegli quella più efficiente.
  • Scrivi il tuo codice nel modo più modulare possibile con interfacce ben definite.

È quindi possibile modificare i meccanismi all'interno di ciascun modulo senza ristrutturare l'intera base di codice quando è necessario ottimizzare.


16
All'inizio puoi anche implementare il tuo traghetto come IRiverCrossing, e quando diventa chiaro che il volume del traffico è troppo grande per il traghetto impostato, implementa il bridge come IRiverCrossinge rilascialo. Il trucco ovviamente sta riducendo l'API esterna del traghetto e il Bridge a funzionalità comuni in modo che possano essere rappresentati dalla stessa interfaccia.
Mr.Mindor,

2
Potresti anche avere dei test automatici scritti per queste interfacce modulari, in modo da poter eseguire rapidamente il refactoring senza preoccuparti di rompere qualsiasi cosa al di fuori del modulo che stai toccando.
user3067860

2
Un bell'esempio del perché OOP è così importante in questi giorni.
Jaime Gallego,

1
Hai colpito la ragione giusta. Il mantra di Donald Knuth è vero solo quando la velocità non è uno dei requisiti fondamentali e un focus sull'ottimizzazione comprometterebbe il design principale. Purtroppo, nei giochi la velocità è spesso al centro, e quindi deve essere presa in considerazione nel design all'inizio. La preoccupazione del PO (e la tua risposta) è del tutto giustificata.
Peter A. Schneider,

3
@AlexandreVaillancourt invece di rispondere al titolo della domanda, ho cercato di rispondere a quello che penso sia la parte chiave della domanda a cui effettivamente si risponde; vale a dire "perché non dovrei ottimizzare in anticipo se l'ottimizzazione sarà molto più lavoro in seguito perché il mio progetto è stato risolto" (parafrasato) . Questa risposta è iniziata anche come commento sulla risposta di DMGregory, ed è in aggiunta ad essa, e non ha molto senso duplicare la sua risposta.
Baldrickk,

24

"Non ottimizzare in anticipo" non significa "scegliere il modo peggiore possibile per fare le cose". Devi ancora considerare le implicazioni delle prestazioni (a meno che tu non stia solo prototipando). Il punto non è quello di paralizzare altre cose più importanti a quel punto nello sviluppo - come la flessibilità, l'affidabilità, ecc. Scegli ottimizzazioni semplici e sicure - scegli le cose che limiti e le cose che mantieni libere; tenere traccia dei costi. Dovresti usare la tipizzazione forte? La maggior parte dei giochi funzionava bene; quanto ti costerebbe rimuoverlo se trovassi interessanti usi della flessibilità per il gamemplay?

È molto più difficile modificare il codice ottimizzato, in particolare il codice "intelligente". È sempre una scelta che rende alcune cose migliori e altre peggiori (ad esempio, potresti scambiare il tempo della CPU per l'utilizzo della memoria). Quando fai questa scelta, devi essere consapevole di tutte le implicazioni: potrebbero essere disastrose, ma possono anche essere utili.

Ad esempio, il comandante Keen, Wolfenstein e Doom sono stati ciascuno costruito su un motore di rendering ottimizzato. Ognuno aveva il suo "trucco" che consentiva al gioco di esistere in primo luogo (ognuno aveva anche ulteriori ottimizzazioni sviluppate nel tempo, ma non è importante qui). Va bene . Va bene ottimizzare fortemente il nocciolo del gioco, il pensiero che rende possibile il gioco; soprattutto se stai esplorando nuovi territori in cui questa particolare funzionalità ottimizzata ti consente di prendere in considerazione progetti di gioco che non sono stati molto esplorati. Le limitazioni introdotte dall'ottimizzazione possono offrire anche un gameplay interessante (ad esempio, i limiti di conteggio delle unità nei giochi RTS potrebbero essere iniziati come un modo per migliorare le prestazioni, ma hanno anche un effetto di gameplay).

Ma nota che in ciascuno di questi esempi, il gioco non potrebbe esistere senza l'ottimizzazione. Non hanno iniziato con un motore "completamente ottimizzato", hanno iniziato con la nuda necessità e sono saliti su. Stavano sviluppando nuove tecnologie e le stavano usando per creare giochi divertenti. E i trucchi del motore erano limitati alla minima parte possibile della base di codice: le ottimizzazioni più pesanti venivano introdotte solo quando il gameplay era per lo più fatto, o dove permetteva l'emergere di una nuova interessante funzionalità.

Ora considera un gioco che potresti voler creare. C'è davvero qualche miracolo tecnologico che fa o distrugge quel gioco? Forse stai immaginando un gioco open world su un mondo infinito. È davvero il pezzo centrale del gioco? Il gioco semplicemente non funzionerebbe senza di esso? Forse stai pensando a un gioco in cui il terreno è deformabile senza limiti, con geologia realistica e simili; puoi farlo funzionare con un ambito più piccolo? Funzionerebbe in 2D anziché in 3D? Divertiti al più presto - anche se le ottimizzazioni potrebbero richiedere di rielaborare un grosso pezzo del tuo codice esistente, potrebbe valerne la pena; e potresti persino capire che rendere le cose più grandi non migliora il gioco.

Come esempio di un gioco recente con molte ottimizzazioni, vorrei indicare Factorio. Una parte fondamentale del gioco sono le cinture: ce ne sono molte migliaia e trasportano molti singoli pezzi di materiale in tutta la tua fabbrica. Il gioco è iniziato con un motore a cinghia fortemente ottimizzato? No! In effetti, il design originale della cintura era quasi impossibile da ottimizzare - in un certo senso ha fatto una simulazione fisica degli oggetti sulla cintura, che ha creato alcune cose interessanti che potresti fare (questo è il modo in cui ottieni un gameplay "emergente" - gameplay che sorprende il designer), ma significava che dovevi simulare ogni singolo oggetto della cintura. Con migliaia di cinture, ottieni decine di migliaia di articoli simulati fisicamente: anche solo rimuovendola e lasciando che le cinture facciano il lavoro, puoi ridurre il tempo della CPU associato del 95-99%, anche senza considerare cose come la località di memoria. Ma è utile farlo solo quando si raggiungono effettivamente questi limiti.

Praticamente tutto ciò che aveva a che fare con le cinture doveva essere rifatto per consentire l'ottimizzazione delle cinture. E le cinture dovevano essere ottimizzate, perché avevi bisogno di molte cinture per una grande fabbrica e le grandi fabbriche sono un'attrazione del gioco. Dopotutto, se non puoi avere grandi fabbriche, perché avere un mondo infinito? Divertente che dovresti chiedere - le prime versioni no :) Il gioco è stato rielaborato e rimodellato molte volte per arrivare dove sono ora - incluso un rifacimento al 100% quando hanno realizzato che Java non è la strada da percorrere per un gioco come questo e passato a C ++. E ha funzionato alla grande per Factorio (anche se era comunque una buona cosa non è stato ottimizzato fin dall'inizio - soprattutto perché si trattava di un progetto di hobby, che altrimenti avrebbe potuto fallire semplicemente per mancanza di interesse).

Ma il fatto è che ci sonomolte cose che puoi fare con una fabbrica di portata limitata - e molti giochi hanno dimostrato proprio questo. I limiti possono essere ancora più potenti per il divertimento delle libertà; Spacechem sarebbe più divertente se le "mappe" fossero infinite? Se avessi iniziato con "cinture" fortemente ottimizzate, saresti praticamente costretto ad andare in quel modo; e non puoi esplorare altre direzioni di progettazione (come vedere quali cose interessanti puoi fare con i nastri trasportatori simulati dalla fisica). Stai limitando il tuo potenziale spazio di progettazione. Potrebbe non sembrare così perché non vedi molti giochi incompiuti, ma la parte difficile è ottenere il divertimento giusto - per ogni gioco divertente che vedi, probabilmente ce ne sono centinaia che semplicemente non potevano arrivarci e venivano scartati (o peggio, rilasciato come disastri orribili). Se l'ottimizzazione ti aiuta a farlo, vai avanti. Se non lo fa ... è probabilmente prematuro. Se ritieni che alcune meccaniche di gioco funzionino alla grande, ma hanno bisogno di ottimizzazioni per brillare davvero, vai avanti. Se non hai meccaniche interessanti,non ottimizzarli . Trova prima il divertimento: scoprirai che la maggior parte delle ottimizzazioni non ti aiutano e spesso sono dannose.

Finalmente hai un gioco fantastico e divertente. Ha senso ottimizzare adesso ? Ha! Non è ancora chiaro come potresti pensare. C'è qualcosa di divertentepuoi fare invece? Non dimenticare che il tuo tempo è ancora limitato. Tutto richiede uno sforzo e tu vuoi concentrarlo su ciò che conta di più. Sì, anche se stai realizzando un "gioco gratuito" o un gioco "open source". Guarda come si gioca; notare dove la performance diventa un collo di bottiglia. L'ottimizzazione di questi luoghi rende più divertente (come costruire fabbriche sempre più grandi, sempre più intricate)? Ti consente di attirare più giocatori (ad es. Con computer più deboli o su piattaforme diverse)? Devi sempre stabilire le priorità: cerca lo sforzo per raggiungere il rapporto. Probabilmente troverai un sacco di frutta a basso consumo solo giocando e guardando gli altri mentre giocano. Ma nota la parte importante: per arrivarci, hai bisogno di un gioco . Concentrati su quello.

Come una ciliegina sulla torta, considera che l'ottimizzazione non finisce mai. Non è un'attività con un piccolo segno di spunta che termina e passa ad altre attività. C'è sempre "un'altra ottimizzazione" che puoi fare e gran parte di ogni sviluppo è capire le priorità. Non si esegue l'ottimizzazione per motivi di ottimizzazione - lo si fa per raggiungere un obiettivo particolare (ad esempio "200 unità sullo schermo contemporaneamente su un Pentium a 333 MHz" è un grande obiettivo). Non perdere la traccia dell'obiettivo terminale solo perché ti concentri troppo sugli obiettivi intermedi che potrebbero non essere più nemmeno prerequisiti per l'obiettivo terminale.


Credo che gli sviluppatori factorio stiano ottimizzando di nuovo le cinture in modo che debbano solo simulare periodicamente gli articoli e in tutte le altre volte interpola la loro posizione quando li guardi. Ma lo stanno facendo solo ora quando l'intero gioco è stato efficacemente basato su cinture (e robot) per molto tempo e non fino a quando le persone hanno iniziato a notare e lamentarsi delle prestazioni.
immibis,

2
@immibis Exactly. Renderlo migliore prima che le persone abbiano effettivamente raggiunto i limiti sarebbe uno spreco di risorse, che sono meglio spesi altrove. Anche con le ultime ottimizzazioni, molto probabilmente incontrerai il problema solo per i megafattori che vanno ben oltre lo scopo del normale gameplay (lanciando il razzo). Ma ha abilitato le nuove impostazioni di gioco Marathon, che richiedono una logistica molto più pesante. E ancora, hanno identificato i colli di bottiglia ottenendo statistiche dalle persone che stavano effettivamente giocando - circa la metà dei colli di bottiglia è stata una grande sorpresa per loro.
Luaan,

@Fattie Quindi capisci la domanda, ma non la risposta? Stai solo cercando di dire che è possibile una risposta più semplice (ad es. "Economia")? Non vedo come il tuo commento dovrebbe essere costruttivo.
Luaan,

2
@Fattie Se pensi che la risposta sia "Ottimizza solo una volta che conosci qual è il collo di bottiglia", pubblicalo come risposta. Hai già speso molte più parole di quelle necessarie per una buona risposta, quindi vai avanti. Che tipo di ottimizzazione non rende le cose migliori e peggiori? Non ne ho mai visto uno. La cosa fondamentale che continuo a ripetere è che è sempre un compromesso - il tempo che potrebbe essere speso meglio altrove, la complessità che limita la tua flessibilità ... Gli esempi sottolineano che "early" non è un termine assoluto; alcune ottimizzazioni sono necessarie dal primo giorno, mentre altre sono dannose.
Luaan,

1
@Fattie "Ottimizza solo quando sai qual è il collo di bottiglia" è la risposta a "Quando dovrei ottimizzare?" e non è una risposta a "Perché è così male ottimizzare troppo presto?".
immibis,

10

Molte delle risposte sembrano concentrarsi molto sull'aspetto prestazionale dell '"ottimizzazione", mentre a me piace guardare l'ottimizzazione e l'intera prova dell'ottimizzazione troppo presto a un livello più astratto.

Umorizzami mentre cerco di elaborare la mia prospettiva con l'aiuto dei poliomino.

Supponiamo di avere dei limiti fissi stabiliti dal framework o dal motore con cui stiamo lavorando.

inserisci qui la descrizione dell'immagine

Quindi procediamo a creare il nostro primo strato / modulo del gioco in questo modo.

inserisci qui la descrizione dell'immagine

Andando avanti costruiamo il nostro secondo strato / modulo.

inserisci qui la descrizione dell'immagine

A questo punto, potremmo notare che c'è uno spazio disponibile tra i due moduli e potremmo essere tentati di ottimizzarlo per sfruttare appieno i confini che ci sono stati assegnati.

inserisci qui la descrizione dell'immagine

Perfetto, ora l'applicazione sta sfruttando appieno le risorse a nostra disposizione, l'applicazione è migliore, va bene?

Procediamo a costruire il terzo strato / modulo della nostra applicazione e improvvisamente arriviamo alla realizzazione (forse anche uno che non avremmo potuto prevedere durante la pianificazione iniziale) che il 3 ° strato / modulo non funziona per noi.

Cerchiamo un'alternativa, ne troviamo una e, alla fine, ciò richiede anche che cambiamo il secondo modulo della nostra applicazione poiché è incompatibile con il nostro terzo modulo appena selezionato. (Per fortuna è in qualche modo compatibile con il nostro primo modulo, quindi non dobbiamo riscrivere tutto da zero.)

Quindi abbiamo messo tutto insieme ...

inserisci qui la descrizione dell'immagine

Vedi cosa è successo?

Ottimizzando troppo presto ora abbiamo effettivamente peggiorato le cose dal punto di vista dell'efficienza, dal momento che ciò che abbiamo ottimizzato non è quello con cui siamo finiti.

E se avessimo voluto aggiungere alcuni moduli aggiuntivi o bocconcini in seguito, potremmo non avere più la capacità di farli a questo punto.

inserisci qui la descrizione dell'immagine

E regolare il livello più basso del nostro sistema non è più così fattibile poiché è stato sepolto sotto tutti gli altri livelli.

Se, tuttavia, avessimo deciso di attendere con il nostro bisogno di ottimizzarlo immediatamente, saremmo finiti con qualcosa del genere:

inserisci qui la descrizione dell'immagine

E ora se eseguiamo l'ottimizzazione a questo punto otteniamo qualcosa di soddisfacente da guardare.

inserisci qui la descrizione dell'immagine

Spero che almeno alcuni di voi si siano divertiti a leggere questo quanto mi sono divertito a farlo :) e se ora avete la sensazione di avere una migliore comprensione dell'argomento - tanto meglio.


3
Sto rimuovendo l'annuncio. Non ci interessa come hai realizzato le immagini; l'unica parte importante è il presupposto che tu abbia il diritto di pubblicarli.
Gnemlock,

2
@Gnemlock è abbastanza equo, mentre la mia intenzione non era rivolta alla pubblicità, posso anche vedere come potrebbe essere facilmente interpretato male come tale, e concorderò che non aggiunge nulla alla risposta, quindi non ha spazio in essa.
Soffitto Geco,

2
Penso che questa metafora visiva sia estremamente efficace. Ottimo approccio per rispondere alla domanda! :)
DMGregory

8

I soldi.

Dipende da quello. I soldi. Dato che il tempo è denaro *, maggiore è il tempo che dedichi ad attività che non sono garantite per generare più denaro (ovvero non puoi considerare queste attività come investimenti ), più soldi rischi di sprecare e meno soldi farai con il gioco.

Ecco alcuni altri potenziali effetti collaterali dell'ottimizzazione troppo precoce e motivi per cui dovresti evitarlo:

  • I tuoi colleghi ti odiano perché giochi nella tua sandbox con i tuoi giocattoli mentre lavorano sodo per ottenere funzionalità.
  • I ritardi non piacciono al management superiore e fanno perdere al team le date di consegna, il che provoca il rilascio del gioco in primavera anziché prima delle festività, il che a sua volta non fa vendere abbastanza il gioco. E per questo motivo, la sede centrale decide di chiudere lo studio, rendendoti effettivamente senza lavoro. E nessun lavoro significa niente soldi. (E poiché non piaci a nessuno perché hai trascorso troppo tempo a fare l'ottimizzazione precoce, nessuno vuole scrivere una recensione positiva sul tuo lavoro per te su LinkedIn, che ti terrà senza soldi per un tempo più lungo.)

* Altre risposte hanno evidenziato abbastanza bene la parte "tempo"


Come nota a margine, in generale , l'esperienza aiuta a determinare cosa è e cosa non è ottimizzazione prematura e cosa ha valore per l'azienda e cosa no.

Ad esempio, se hai lavorato sul gioco A e ti sei reso conto alla fine del progetto che la funzione XYZ era particolarmente pesante nel tuo ciclo di gioco e alla fine inizi a lavorare sul gioco B che ha la stessa identica funzione, decidendo di riscrivere la funzionalità e ottimizzarla dall'inizio non è in realtà un'ottimizzazione prematura, dato che sai che sarà un collo di bottiglia se non si fa nulla.


1
@JBentley questo è un punto valido. Tuttavia, esiste una risposta consequenziale al "perché" come diretta conseguenza dell'esperienza acquisita. Quindi, 1) l'ottimizzazione precoce può portare a sprecare tempo su parti del codice che non influiscono sul tempo di esecuzione medio (data l'esperienza, dovresti sapere quali costrutti di codice sono candidati per le migliori pratiche di ottimizzazione) 2) l'ottimizzazione precoce può rendere il codice più difficile mantenere a causa delle restrizioni di progettazione imposte su alcuni sistemi. Potresti quindi guadagnare troppo poco in cambio di cicli di sviluppo più lunghi / codice ingombrante da mantenere.
teodron,

3
@JBentley Considera questo come un addendum alla risposta di DMGregory.
Alexandre Vaillancourt

1
Questa sembrerebbe essere l'unica risposta utile qui. La domanda è una tautologia. Potresti anche chiedere "Allora perché, in realtà, vuoi che un gioco faccia più soldi che meno sugli app store?" Come puoi rispondere?
Fattie,

@Fattie È bello avere questo tipo di prospettiva in una risposta. Ma dire che è l'unica risposta utile è restringere l'ambito della domanda irragionevolmente. Stai assumendo, ad esempio, che gli unici giochi realizzati siano all'interno di organizzazioni a scopo di lucro (chiaramente non vero). Supponete anche che sia ovvio per l'OP che l'ottimizzazione anticipata porta a più tempo speso (e quindi più denaro se stiamo parlando di un'azienda). In realtà la domanda del PO mostra che non ne è sicuro.
JBentley,

6

L'ottimizzazione è, per definizione, il processo di aumento dell'efficienza di una soluzione fino al punto in cui perde la sua efficacia. Questo processo implica quindi una riduzione dello spazio della soluzione.

In una fase iniziale di sviluppo del software possono esserci ancora "requisiti nascosti": se si riduce troppo lo spazio della soluzione, si potrebbe finire in una situazione in cui non si è in grado di soddisfare un "requisito nascosto" quando si apre in una fase successiva di sviluppo, costringendoti quindi a modificare l'architettura aggiungendo in questo modo instabilità e un possibile gruppo di comportamenti indesiderati.

L'idea è quindi di far funzionare l'intera soluzione e solo allora, quando tutti i requisiti sono fissi e implementati, stringono il codice. Vedrai quindi che molte ottimizzazioni che avresti implementato con leggerezza contemporaneamente durante la codifica, ora non sono più realizzabili a causa dell'interazione con requisiti tardivi.

Lo spazio reale della soluzione è sempre più grande di quello che ci aspettiamo all'inizio perché non possiamo avere una perfetta conoscenza di un sistema molto complesso.

Fallo funzionare prima. Quindi stringere le corde.


2
Penso che "efficacia" non sia la parola che stai cercando - significa "il potere di produrre un effetto", quindi, in un certo senso, l'ottimizzazione si basa sull'aumento dell'efficacia. Stai cercando una versione specifica dell'efficacia? (Potresti collegarti ad esso?) Intendi invece qualcosa come flessibilità?
doppelgreener,

2
@doppelgreener Significa che rendi la soluzione più efficiente fino a quando non smette di produrre gli effetti desiderati ...
immibis

1
"Fallo funzionare prima. Quindi stringi le corde." - Deve valere tanto quanto il resto della risposta combinato. E per non dire che le altre cose non erano buone.
ebyrob

1
@ebyrob: c'è un altro detto: "Fallo funzionare, fallo nel
ninjalj

@ninjalj Anche questo è buono. Certo, il mio capo mi dice sempre: "Non abbiate fretta, fatelo bene". Quindi non so se quello che stai dicendo sia universale come il principale su cui il poster originale richiede maggiori dettagli.
ebyrob,

2

In breve, molto spesso l'ottimizzazione precoce diventa uno sforzo sprecato se si desidera cambiare le cose in seguito, e poi si scopre di aver ottimizzato le strutture di codice facilmente modificabili in qualcosa di livello molto più basso, e ora è necessario riportarlo ad un livello elevato approccio di livello e ottimizzazione del risultato ancora una volta.

Questo è un errore comune per gli sviluppatori principianti a cui piace concentrarsi sul piacere di "aver fatto un lavoro utile", come l'ottimizzazione, non pensare a se è il momento giusto per farlo. Man mano che acquisisci esperienza nella programmazione di grandi progetti come i giochi, imparerai quando è garantito l'ottimizzazione della base di codice esistente e quando è troppo presto. Non abbiate paura di fare questo errore, ne trarrete beneficio solo imparando da esso.

In generale, ottimizza solo se non puoi davvero lavorare con la build di sviluppo in questo momento. Ad esempio, se stai creando ed eliminando milioni di oggetti 60 volte al secondo.

Quindi, direi che è buono, in termini di esperienza di apprendimento, ottimizzare presto alcune volte: p


2

L'ottimizzazione si concentra su come far funzionare meglio il computer sul codice mentre lo sviluppo richiede che il programmatore funzioni meglio sul codice.

L'ottimizzazione non fa emergere approfondimenti. Fa lavorare meno il computer e il programmatore di più.

Certo, ci sono diverse categorie, come nel progettare un'auto. Non c'è niente di male nell'ottimizzare un motore prima di costruire il tuo primo telaio, ma l'ottimizzazione del telaio prima di non conoscere la forma del motore potrebbe far perdere tempo. Modularità e specifiche possono rendere fattibile un certo numero di posti per il lavoro di ottimizzazione ancor prima che il prodotto nel suo insieme venga assemblato.


Ciò sarebbe migliorato guidando con qualcosa di diverso dalla definizione dell'ottimizzazione e parlando della situazione pratica nello sviluppo del gioco piuttosto che cercare analogie sulle auto.
doppelgreener,

Una risposta perfetta a una domanda tautologica.
Fattie,

2

Sono fortemente in disaccordo con tutte queste affermazioni secondo cui il codice non dovrebbe essere ottimizzato nelle fasi iniziali. Dipende da quale fase sei. Se questo è MVP - prototipo e stai solo cercando di guardare il gioco che dura da 1 a 2 settimane, sei pronto a riscrivere tutto ciò che hai già scritto. Sì, l'ottimizzazione non ha importanza. Ma se stai già lavorando su un gioco che sai che verrà rilasciato, dovrebbe avere sempre un codice ottimizzato. Le storie che la gente dice su nuove funzionalità e cose che potrebbero essere aggiunte è un errore di concezione. È un'architettura di codice mal progettata piuttosto che un codice ottimizzato. Non è necessario riscrivere nulla per aggiungere nuovi elementi se si dispone di un buon design del codice.

Ad esempio, se si dispone di un algoritmo di pathfinding A *, non sarebbe meglio scriverlo nel modo più ottimale possibile? Invece di cambiare la metà del codice che hai in seguito perché hai dovuto apportare alcune modifiche all'algoritmo perché ora ha bisogno di un altro metodo per chiamate e callback? E se hai già un codice ottimizzato dall'inizio ti farà risparmiare un sacco di tempo. Perché puoi disegnare te stesso le relazioni tra gli oggetti e il modo in cui interagiscono, invece di creare ciecamente tonnellate di nuovi script e creare immediatamente tutte le connessioni - questo porta a un codice sphagetti poco chiaro.

L'ultima cosa che voglio aggiungere è che in seguito, anche se non vuoi più creare il gioco, hai il tuo algoritmo A * ottimizzato che puoi usare per i tuoi prossimi giochi. Riusabilità. Ad esempio, molti giochi hanno inventario, ricerca di percorsi, generazione procedurale, interazioni tra PCN, sistema di combattimento, AI, Interazione interfaccia, gestione input. Ora dovresti chiederti: "Quante volte ho riscritto quegli elementi da zero?" Sono il 70-80% del gioco. Hai davvero bisogno di riscriverli tutto il tempo? E se non fossero ottimizzati?


5
Se il codice A * deve iniziare ottimizzato, quanto viene ottimizzato "ottimizzato"? Prima di fare parametri di riferimento, esegui la codifica manuale delle cose in assembly? Penso che il lavoro in genere non banale sulle microottimizzazioni dovrebbe essere lasciato fino a quando non saranno stati eseguiti i benchmark. Il compilatore di ottimizzazione potrebbe fare un lavoro migliore di te alle microottimizzazioni ed è molto più economico.
Gmatht,

@gmatht Bene A * può essere diverso poiché ho detto che può avere un design scadente e non funzionare molto bene. Ma può essere multithread, basato sull'heap di Fibonacci, avere alcune previsioni, diviso in blocchi. Se stiamo parlando di pathfinding, possiamo aggiungere Flow Field da miscelare con A *. Anche levigante, multi altezza. Forse A * non è il miglior esempio, ma come ho detto, le ottimizzazioni non hanno nulla a che fare con la modifica effettiva del codice, lo sviluppatore sta cambiando il codice perché è stato progettato male, ad esempio non ha seguito SOLID per 1 motivo. ho scritto questo A * bene, non è più necessario scriverlo.
Candid Moon _Max_

Le micro ottimizzazioni di @gmatht non sono in realtà il problema. Penso che OP significhi più un modo migliore e più efficiente per calcolare il comportamento dell'IA o shader o qualsiasi altra cosa.
Candid Moon _Max_

Se sai come scrivere queste cose in modo efficiente, non vedo il problema farlo. Ci vorrà la stessa quantità di tempo o anche meno. Dipende maggiormente dall'esperienza degli sviluppatori. Come programmatore, se conosco una soluzione migliore, non ne scriverei una schifosa. Se so scrivere l'heap di Fibonacci, non userò Elenco e ordinamento per ottenere i valori minimo o massimo. Ovviamente ci vorrà un po 'più di tempo e fatica per scriverlo, invece di usare List.Sort (); , ma se ne avessi già uno riutilizzabile?
Candid Moon _Max_

2
@CandidMoon Non sono d'accordo con "Ci vorrà la stessa quantità di tempo o anche meno". per scrivere un codice efficiente. Forse ci vorrebbe lo stesso tempo per scrivere codice che non è ovviamente inefficiente, ma quando la tua nozione di "efficienza" significa considerare la contesa della cache, aggiungere il multithreading, usare intrinseci specifici della CPU disponibili solo su alcune CPU, cambiare algoritmi per N di grandi dimensioni a causa di O (N log N) vs O (N): questo tipo di cose sono difficili e soggette a errori e richiedono molto più tempo della semplice implementazione. Lo fai solo quando sai che influenzerà materialmente il risultato finale.
Michael Anderson,
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.