Quando l'ottimizzazione non è prematura e quindi non è malvagia?


75

"L'ottimizzazione precoce è la radice di tutti i mali" è qualcosa che quasi tutti noi abbiamo sentito / letto. Ciò che mi incuriosisce di quale tipo di ottimizzazione non è prematuro, ovvero in ogni fase dello sviluppo del software (progettazione di alto livello, progettazione dettagliata, implementazione di alto livello, implementazione dettagliata ecc.) Qual è l'entità dell'ottimizzazione che possiamo considerare senza che si passi al lato oscuro.



Risposte:


116

Quando lo stai basando sull'esperienza? Non male. "Ogni volta che abbiamo fatto X, abbiamo subito un brutto colpo di prestazione. Pianifichiamo di ottimizzare o evitare X interamente questa volta."

Quando è relativamente indolore? Non male. "L'implementazione di questo come Foo o Bar richiederà altrettanto lavoro, ma in teoria Bar dovrebbe essere molto più efficiente. Facciamolo."

Quando stai evitando algoritmi schifosi che si ridimensioneranno terribilmente? Non male. "Il nostro responsabile tecnico afferma che il nostro algoritmo di selezione del percorso proposto viene eseguito in tempi fattoriali; non sono sicuro di cosa significhi, ma lei suggerisce che ci impegniamo seppuku anche solo per considerarlo. Consideriamo qualcos'altro."

Il male deriva dallo spendere un sacco di tempo e problemi di risoluzione dell'energia che non conosci realmente esistono. Quando i problemi sicuramente esistono, o quando i fantasmi psudo-problemi possono essere risolti a buon mercato, il male scompare.


Steve314 e Matthieu M. sollevano punti nei commenti che dovrebbero essere considerati. Fondamentalmente, alcune varietà di ottimizzazioni "indolore" semplicemente non ne valgono la pena, perché il banale aggiornamento delle prestazioni che offrono non vale l'offuscamento del codice, stanno duplicando i miglioramenti che il compilatore sta già eseguendo, o entrambi. Vedi i commenti per alcuni esempi di non miglioramenti troppo intelligenti della metà.


22
Occasionalmente, risolvere facilmente un problema fantasma è ancora leggermente malvagio, poiché può risultare più difficile da leggere, più difficile da mantenere il codice. Non molto più difficile (o non sarebbe una soluzione facile), ma forse a volte ancora rilevante. Un esempio potrebbe essere l'uso di un trucco bit per bit intelligente che alcune persone non riconosceranno e che il compilatore probabilmente applicherà comunque se è utile.
Steve314,

26
Sono d'accordo con Steve qui, a volte semplicemente "l'ottimizzazione" non ne vale la pena, soprattutto perché i compilatori sono così dannatamente buoni. Esempio ? se inon è firmato, i / 2può essere sostituito da i >> 1. È più veloce. Ma è anche più criptico (non tutti vedranno l'effetto, anche quelli che lo fanno potrebbero perdere tempo). Ma la cosa peggiore è che il compilatore lo farà comunque, quindi perché offuscare il codice sorgente;)?
Matthieu M.

19
@Larry: non l'ho fatto, quindi immagino sia un buon esempio.
Joris Meys,

18
A mio avviso, anche le ottimizzazioni, anche quelle semplici, dovrebbero essere considerate malvagie se incidono sulla leggibilità / manutenzione del codice e non si basano su misurazioni effettive delle prestazioni.
Bart van Ingen Schenau,

14
@Matthew: insegnate loro cosa? Trucchi sporchi e inutili? Perché? Se la profilazione mostra che a i/2è davvero un punto caldo e che (incredibile, ma supponiamo) lo i>>1rende più veloce, quindi fallo e aggiungi un commento che questa profilazione ha mostrato che questo è più veloce. Se questo è davvero necessario ovunque (che dubito, poiché, come ha detto Matthieu, i compilatori dovrebbero essere abbastanza intelligenti da farlo da soli), i principianti impareranno qualcosa, se non lo è (che è probabile), perché vuoi collegare le loro teste con folklore non necessario?
sabato

38

Il codice dell'applicazione dovrebbe essere solo il necessario, ma il codice della libreria dovrebbe essere il più buono possibile, poiché non si sa mai come verrà utilizzata la libreria. Quindi, quando scrivi il codice della libreria, deve essere buono sotto tutti gli aspetti, sia esso prestazioni, robustezza o qualsiasi altra categoria.

Inoltre, devi pensare alle prestazioni quando progetti la tua applicazione e quando scegli gli algoritmi . Se non è progettato per essere performante, nessun livello di hacker può renderlo performante in seguito e nessuna micro-ottimizzazione supererà un algoritmo superiore.


5
Il codice della biblioteca dovrebbe documentare se sta cercando di essere "il più buono possibile" o quale sia il suo obiettivo. Il codice non deve essere assolutamente ottimale per essere utile, a condizione che i consumatori lo utilizzino solo quando appropriato.
supercat,

1
Siamo spiacenti, ma "essere bravi in ​​tutti gli aspetti" suona sospettosamente come una ingegnerizzazione eccessiva. Inoltre, probabilmente non è realistico: la vita è sempre fatta di compromessi.
sleske,

1
+1 per enfatizzare la fase di progettazione; se sopporti deliberatamente i suoi benefici, non è prematuro.
Nathan Tuggy,

Al contrario, se non sai mai come verrà utilizzata la tua biblioteca, non sai se passare del tempo a migliorarla abbia qualche valore commerciale. Quindi non è certo un argomento.
Remco Gerlich,

25

che tipo di ottimizzazione non è prematura

Il tipo che deriva da problemi noti.


17

Quando l'ottimizzazione non è prematura e quindi non è malvagia?

È difficile dire cosa sia il bene e il male. Chi ha questo diritto? Se guardiamo alla natura, sembra che siamo programmati per la sopravvivenza con un'ampia definizione di "sopravvivenza" che include il passaggio dei nostri geni alla prole.

Quindi direi, almeno secondo le nostre funzioni e programmazioni di base, che l'ottimizzazione non è malvagia quando si allinea all'obiettivo della riproduzione. Per i ragazzi, ci sono le bionde, le brune, le teste rosse, molte adorabili. Per le ragazze, ci sono ragazzi e alcuni sembrano stare bene.

Forse dovremmo essere ottimisti verso quello scopo, e lì aiuta a usare un profiler. Il profiler ti consentirà di stabilire le priorità delle ottimizzazioni e del tempo in modo più efficace, oltre a fornirti informazioni dettagliate sugli hotspot e sul motivo per cui si verificano. Questo ti darà più tempo libero speso per la riproduzione e la sua ricerca.


3
È rinfrescante vedere qualcuno portare un nuovo assaggio a questa vecchia castagna. Basta leggere l'intera citazione di Knuth e non solo una frase, eh?
Robert Harvey,

1
@RobertHarvey Ho un po 'di pipì da compagnia lì - dal momento che molti sembrano citare solo quella frase e così tante informazioni contestuali importanti finiscono per perdersi nel processo. Non sono sicuro che sia un'ottima risposta da quando ho avuto un po 'di fretta. :-D

14

Il preventivo completo definisce quando l'ottimizzazione non è prematura:

Un buon programmatore non si crogiolerà in tale compiacimento, sarà saggio guardare attentamente il codice critico; ma solo dopo che quel codice è stato identificato . [enfasi mia]

È possibile identificare il codice critico in molti modi: strutture o algoritmi di dati critici (ad esempio, utilizzati pesantemente o il "nucleo" del progetto) possono fornire importanti ottimizzazioni, molte ottimizzazioni minori vengono identificate tramite i profilatori e così via.


6
Sì ... Va tutto bene e risparmia il 90% del tempo impiegato da una chiamata di funzione casuale, ma forse avrai un impatto maggiore guardando il codice in cui la tua app trascorre effettivamente l'80% del suo tempo e radendo un qualche percento lì.

11

Devi sempre scegliere una soluzione "abbastanza buona" in tutti i casi in base alle tue esperienze.

Il detto di ottimizzazione si riferisce alla scrittura di "codice più complesso di" abbastanza buono "per renderlo più veloce" prima di sapere effettivamente che è necessario, quindi rendere il codice più complesso del necessario. La complessità è ciò che rende le cose difficili, quindi non è una buona cosa.

Ciò significa che non dovresti scegliere un super complesso "puoi ordinare i file da 100 Gb scambiando in modo trasparente su disco" la routine di ordinamento quando un ordinamento semplice farà, ma dovresti anche fare una buona scelta per l'ordinamento semplice in primo luogo. Scegliere ciecamente Bubble Sort o "selezionare tutte le voci in modo casuale e vedere se sono in ordine. Ripetere." è raramente buono.


3

La mia regola generale: se non sei sicuro di aver bisogno dell'ottimizzazione, supponi di no. Ma tienilo a mente quando devi ottimizzare. Ci sono alcuni problemi che puoi conoscere in anticipo però. Questo di solito comporta la scelta di buoni algoritmi e strutture di dati. Ad esempio, se è necessario verificare l'appartenenza a una raccolta, si può essere abbastanza sicuri di aver bisogno di un qualche tipo di struttura di dati impostata.


3

Nella mia esperienza, nella fase di implementazione dettagliata la risposta sta nella profilazione del codice. È importante sapere cosa deve essere più veloce e cosa è accettabilmente veloce.

È anche importante sapere esattamente dove si trova il collo di bottiglia delle prestazioni: l'ottimizzazione di una parte del codice che richiede solo il 5% del tempo totale per l'esecuzione non farà nulla di buono.

I passaggi 2 e 3 descrivono l'ottimizzazione non prematura:

  1. Fallo funzionare
  2. Test. Non abbastanza veloce? Profilalo .
  3. Utilizzando i dati del passaggio 2, ottimizza le sezioni più lente del codice.

Hai dimenticato il passaggio 0, ovvero: progettare correttamente l'applicazione in modo da poter prevedere prestazioni ragionevoli dall'inizio.
Robert Harvey,

Stavo solo parlando della fase di implementazione dettagliata.
Gorgi Kosev

1
Metto in dubbio il passaggio n. 3: molto spesso la risposta migliore è capire un approccio diverso, quindi non stai facendo il lento bit di codice.
Loren Pechtel,

1
Scegli le giuste strutture dati.
Jasonk,

3

Non si tratta di ottimizzazione quando si scelgono cose che sono difficili da cambiare, ad esempio: piattaforma hardware.

La scelta delle strutture di dati è un buon esempio, fondamentale per soddisfare i requisiti sia funzionali che non funzionali (prestazioni). Non facilmente modificabile e tuttavia guiderà tutto il resto nella tua app. Le tue strutture dati cambiano gli algoritmi disponibili ecc.


3

Conosco solo un modo per rispondere a questa domanda, e cioè fare esperienza nell'ottimizzazione delle prestazioni. Ciò significa: scrivere programmi e, dopo averli scritti, trovare gli speedups in essi e farlo in modo iterativo. Ecco un esempio.

Ecco l'errore che molte persone commettono: cercano di ottimizzare il programma prima di eseguirlo. Se hanno frequentato un corso di programmazione (da un professore che in realtà non ha molta esperienza pratica) avranno gli occhiali colorati Big-O, e penseranno che è di questo che si tratta . È lo stesso problema, l'ottimizzazione preventiva. **

Qualcuno ha detto: prima fai la cosa giusta, poi fai in fretta. Avevano ragione.

Ma ora per il kicker: se lo hai fatto un paio di volte, riconosci le cose sciocche che hai fatto in precedenza che causano problemi di velocità, quindi istintivamente le eviti. (Cose come rendere la struttura della tua classe troppo pesante, essere inondato di notifiche, confondere le dimensioni delle chiamate di funzione con il loro costo in tempo, l'elenco potrebbe continuare all'infinito ...) Le eviti istintivamente, ma indovina come si presenta al meno- esperto: ottimizzazione prematura!

Quindi questi sciocchi dibattiti continuano all'infinito :)

** Un'altra cosa che dicono è che non devi più preoccupartene, perché i compilatori sono così buoni e le macchine sono così veloci al giorno d'oggi. (KIWI - Kill It With Iron.) Non ci sono accelerazioni esponenziali dell'hardware o del sistema (eseguite da ingegneri molto intelligenti che lavorano sodo) che possono eventualmente compensare rallentamenti esponenziali del software (eseguiti da programmatori che la pensano in questo modo).


2

Quando le esigenze o il mercato lo richiedono specificamente.

Ad esempio, le prestazioni sono un requisito nella maggior parte delle applicazioni finanziarie perché la bassa latenza è cruciale. A seconda della natura dello strumento scambiato, l'ottimizzazione può passare dall'uso di algoritmi non bloccanti in un linguaggio di alto livello all'utilizzo di un linguaggio di basso livello e persino dall'estremo - implementando gli algoritmi di corrispondenza dell'ordine nell'hardware stesso (usando FPGA per esempio ).

Un altro esempio potrebbe essere alcuni tipi di dispositivi integrati. Prendiamo ad esempio il freno ABS; in primo luogo c'è la sicurezza, quando si colpisce la pausa l'auto dovrebbe rallentare. Ma c'è anche una prestazione, non vorresti alcun ritardo quando colpisci la pausa.


0

La maggior parte delle persone chiamerebbe l'ottimizzazione prematura, se stai ottimizzando qualcosa che non si traduce in un "errore soft" (funziona ma è ancora inutile) del sistema a causa delle prestazioni.

Esempi del mondo reale.

  • Se il mio ordinamento a bolle impiega 20 ms per essere eseguito, ottimizzarlo a 1 ms quicksort non migliorerà l'utilità generale in alcun modo significativo nonostante sia un aumento delle prestazioni del 2000%.

  • Se una pagina Web impiega 20 secondi per caricarsi e la riduciamo a 1 secondo, ciò può aumentare l'utilità del sito Web da 0 a quasi infinito. Fondamentalmente qualcosa che è stato rotto perché troppo lento, ora è utile.


È importante notare che se il tuo ordinamento viene chiamato 1000 volte nel corso del programma, l'ottimizzazione da 20ms a 1ms è un grosso problema.
Beefster,

0

Che tipo di ottimizzazione non è prematura?

Un'ottimizzazione che risolve un problema di prestazioni noto con l'applicazione o un'ottimizzazione che consente all'applicazione di soddisfare criteri di accettazione ben definiti.

Una volta identificato, è necessario dedicare un po 'di tempo per stabilire la correzione e misurare il vantaggio in termini di prestazioni.

(cioè non è - "Penso che questo pezzetto di codice possa sembrare lento, cambierò X per usare Y invece e sarà più veloce").

Ho riscontrato molte "ottimizzazioni" premature che alla fine hanno rallentato il codice - in questo caso, prendo prematuro per significare "non pensato". Le prestazioni devono essere confrontate prima e dopo l'ottimizzazione e deve essere mantenuto solo il codice che effettivamente migliora le prestazioni.


0

"L'ottimizzazione prematura è la radice di tutti i mali" è qualcosa che quasi tutti noi abbiamo sentito / letto

Vero. Sfortunatamente è anche una delle citazioni di programmazione più (maliziosamente) abusate di tutti i tempi. Dato che Donald Knuth ha coniato il meme vale la pena aggiungere un po 'di contesto originale dalla citazione:

Dobbiamo dimenticare le piccole efficienze, diciamo circa il 97% delle volte: l'ottimizzazione prematura è la radice di tutti i mali. Tuttavia non dovremmo rinunciare alle nostre opportunità in quel 3% critico. ... Un buon programmatore ... sarà saggio esaminare attentamente il codice critico; ma solo dopo che quel codice è stato identificato. ... l'esperienza universale dei programmatori che hanno utilizzato strumenti di misurazione è stata che le loro ipotesi intuitive falliscono

Si noti che Knuth ha parlato specificamente della velocità di esecuzione in fase di esecuzione .

I programmatori sprecano enormi quantità di tempo pensando o preoccupandosi della velocità delle parti non critiche dei loro programmi.

Inoltre, ha scritto l'articolo nel 1974, quando qualsiasi risorsa di macchina in cui la correlazione premium e negativa tra la velocità di esecuzione e la manutenibilità del programma (maggiore velocità - meno gestibile) era probabilmente più forte di adesso.

OK, per rispondere alla tua domanda, secondo Donald Knuth, l'ottimizzazione NON è prematura se corregge un grave collo di bottiglia delle prestazioni che è stato identificato (idealmente misurato e individuato durante la profilazione).

Come ho detto prima, "l'ottimizzazione prematura" è uno dei meme più maltrattati, quindi la risposta non sarà completa senza alcuni esempi di cose che non sono ottimizzazioni premature ma a volte vengono scrollate di dosso come tali:

  • strozzature che sono visibili ad occhio nudo e possono essere evitate prima di essere introdotte come O (N ^ 2) numero di roundtrip al database con N grande dove esiste un'alternativa O (1)

Inoltre non sono nemmeno correlati alla velocità di esecuzione del runtime:

  • design premuroso in anticipo

  • digitazione statica (!)

  • ecc. / qualsiasi forma di sforzo mentale

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.