Perché dovrei preoccuparmi di micro prestazioni ed efficacia?


71

Molte domande e risposte sulle pagine C / C ++, in particolare o indirettamente, discutono problemi di micro prestazioni (come il sovraccarico di una funzione indiretta vs diretta vs in linea) o usando un algoritmo O (N 2 ) vs O (N log N) su un elenco di 100 articoli.

Codifico sempre senza preoccupazioni per le micro prestazioni e poca preoccupazione per le prestazioni macro, concentrandomi su un codice facile da mantenere e affidabile, a meno che o fino a quando non so di avere un problema.

La mia domanda è: perché a così tanti programmatori importa così tanto? È davvero un problema per la maggior parte degli sviluppatori, ho avuto la fortuna di non dovermi preoccupare troppo o sono un cattivo programmatore?


5
+1, buona domanda generale.
iammilind,

+1 buona domanda..ho aggiunto 2 tag .. spero che non ti dispiaccia.

2
Ho a capo due grandi citazioni 1) "L'ottimizzazione precoce è la radice di tutti i mali." 2) L'80% del tuo tempo sarà speso del 20% del tuo codice (regola 80/20).
James Khoury,

2
Vedo un paio di risposte che parlano del mio esempio O (n * n). Ho specificato esplicitamente un elenco di 100 elementi, ma insistono ancora sul fatto che O (nlogn) sia migliore, affermando esplicitamente miglioramenti delle prestazioni se l'elenco, nel futuro, va a 1000 o milioni. Questa ossessione per la micro ottimizzazione perché i programmatori stanno programmando i possibili requisiti futuri piuttosto che i requisiti attuali attuali? (Dove l'ho già sentito prima ...)
mattnz,

5
@James la citazione completa di Donald Knuth è "Dovremmo dimenticare le piccole efficienze, diciamo circa il 97% delle volte: l'ottimizzazione prematura è la radice di tutti i mali". Ci saranno alcune buone risposte sul restante 3% in questa discussione.
StuperUser,

Risposte:


14

In pratica, le prestazioni raramente sono un problema che deve essere gestito a quel livello di dettaglio. Vale la pena tenere d'occhio la situazione se sai che stai per archiviare e manipolare enormi quantità di dati, ma per il resto, hai ragione e stai meglio, mantenendo le cose semplici.

Una delle trappole più facili in cui cadere - specialmente in C e C ++ dove hai un controllo così preciso - è l'ottimizzazione troppo presto e ad un livello troppo fine. In generale la regola è: A) non ottimizzare fino a quando non si scopre di avere un problema, e B) non ottimizzare nulla che non si è dimostrato essere un'area problematica utilizzando un profiler.

Un corollario di B) è: i programmatori sono notoriamente cattivi nel predire dove i loro colli di bottiglia nelle prestazioni sono, anche se, a uno, pensano di essere bravi a farlo. Usa un profiler e ottimizza le parti lente o cambia gli algoritmi se una sezione del codice viene chiamata troppe volte, in modo da causare un problema.


6
Un altro: il codice di inizializzazione che viene eseguito una volta in genere non necessita di ottimizzazione, quindi cerca altrove.
Mike DeSimone,

3
Dipende da quanto spesso "una volta" è. Durante l'esecuzione ./configure, mi permetto di dire che fino al 75% del tempo di esecuzione potrebbe essere speso per il codice di "inizializzazione" nei programmi eseguiti dallo script. Il 25-50% potrebbe persino essere speso per il collegamento dinamico.
R ..

12
La regola A è una regola terribile. L'architettura di un sistema gioca un ruolo nelle prestazioni e, se scopri in seguito la tua architettura, semplicemente non è in grado di supportare i tuoi requisiti di prestazione, sei praticamente incasinato. Quindi, mentre si è in grado di trasmettere dettagli precisi, ignorarlo completamente all'inizio è semplicemente sbagliato.
edA-qa mort-ora-y

3
@ edA-qa: lo pensavo, ma nel corso degli anni ho sperimentato molti altri progetti che hanno interrotto o fallito prima che qualsiasi considerazione sulle prestazioni diventasse una preoccupazione. Ogni volta che ho avuto problemi di prestazioni, la correzione è stata relativamente a basso costo, giorni o poche settimane, niente di più di qualsiasi altro "bug" rilevato una correzione nello sviluppo. Tuttavia, come per ogni altro elemento a rischio, è necessario identificare e mitigare i coneri delle prestazioni all'inizio del progetto.
mattnz,

5
L'OP ha chiesto perché così tanti si preoccupano e non riesco a vedere come questa risposta abbia effettivamente risposto alla domanda, a meno che l'OP non fosse più interessato a sentire qualcuno dire "non preoccuparti!".
terra rossa

54

Penso che tutto nella tua lista sia una micro-ottimizzazione, che generalmente non dovrebbe essere considerata, tranne

usando un algoritmo O (n * n) vs O (NlogN) in un elenco di 100 voci

che penso dovrebbe essere guardato. Certo, quella lista contiene 100 articoli in questo momento, e tutto è veloce per la piccola n , ma sarei disposto a scommettere presto che lo stesso codice verrà riutilizzato per un elenco di diversi milioni di righe, e il codice continuerà ad avere lavorare ragionevolmente.

Scegliere l'algoritmo giusto non è mai una micro-ottimizzazione. Non si sa mai che tipo di dati verrà utilizzato lo stesso codice per due mesi o due anni dopo. A differenza delle "microottimizzazioni" che sono facili da applicare con la guida di un profiler, le modifiche agli algoritmi richiedono spesso una riprogettazione significativa per un uso efficace dei nuovi algoritmi. (Ad esempio alcuni algoritmi richiedono che i dati di input siano già ordinati, il che potrebbe costringerti a modificare parti significative delle tue applicazioni per garantire che i dati rimangano ordinati)


36
+1 per "Scegliere l'algoritmo giusto non è mai una micro-ottimizzazione."

9
Anch'io avrei fatto +1, ma nota che scegliere l'algoritmo big-O-ottimale quando le dimensioni dei tuoi dati sono sicuramente ridotte può essere dannoso per il tempo di sviluppo, la dimensione del programma e forse anche l'uso della memoria. Se stai ordinando le mani di poker, vuoi davvero scrivere un quicksort, smoothsort o mergesort? Vorrei iniziare con un semplice ordinamento per inserzione o utilizzare una rete di ordinamento.
R ..

8
È divertente. In un thread sulla micro-ottimizzazione, molti commentatori micro-ottimizzano le risposte. ;)
Sicuro

5
"Sarei disposto a scommettere presto che lo stesso codice verrà riutilizzato per un elenco di diversi milioni di righe": dipende completamente dal dominio del problema. Esempi: se stai scrivendo un algoritmo di scacchi, puoi essere ragionevolmente sicuro che la dimensione della scacchiera non cambierà. Se si programma un veicolo autonomo, il numero di ruote non crescerà così velocemente.
nikie,

3
Non mi piace "scegliere l'algoritmo giusto non è mai una micro-ottimizzazione" perché è OVVIAMENTE vero, data la natura della parola "giusto". Tuttavia, ritengo che la tua implicazione sia davvero l'algoritmo "più veloce o più efficiente", con cui non sono d'accordo. Scegliere l'algoritmo più efficiente è la scelta sbagliata se ci vuole un sacco di tempo per implementare e la velocità o lo spazio di quel segmento difficilmente importa comunque.
Casey Patton,

18

Molto tempo fa, nel mio primo lavoro, ho scritto codice per sistemi embedded. Questi sistemi utilizzavano 8086 microprocessori e avevano una memoria limitata. Abbiamo usato il compilatore Intel C. Un sistema da me creato era necessario per accedere a una serie di strutture 3D. L'ho costruito proprio come mi diceva il libro: chiama malloc per le 3 dimensioni, quindi assegna le righe per la dimensione successiva, quindi calloc per i nodi finali.

Era piuttosto complicato (per me al momento), dovevo fare adattamento alla curva, controllo del processo ANOVA e analisi del Chi-quadrato. Non c'erano biblioteche che facessero questo per noi; abbiamo dovuto scrivere tutto e adattarlo tutto sull'8086.

Il sistema funzionava come un cane. Dopo una rapida profilazione, ho scoperto che uno dei maggiori problemi era l'allocatore. Per risolvere il problema ho abbandonato tutte le chiamate a malloc e ho fatto la mia gestione della memoria di un grande blocco di memoria.


In un altro caso nello stesso lavoro, il cliente si lamentava dei tempi di risposta sul proprio sistema di controllo statistico dei processi. Il team prima di me aveva progettato un sistema "software PLC" in cui gli operatori potevano utilizzare una logica booleana per combinare segnali e interruttori di intervento. Lo hanno scritto in un linguaggio semplificato, quello che oggi chiameremmo un "linguaggio specifico di dominio". come ricordo, sembrava ((A1 + B1) > 4) AND (C1 > C2)e così via.

Il design originale analizzava e interpretava quella stringa ogni volta che veniva valutata. Sul nostro misero processore, ciò ha richiesto molto tempo e ha significato che il controller di processo non è stato in grado di eseguire l'aggiornamento rapidamente mentre il processo era in esecuzione.

Ho dato una nuova occhiata a questo e ho deciso che avrei potuto tradurre quella logica in codice assembly, in fase di esecuzione. L'ho analizzato una volta e poi ogni volta che è stato eseguito, l'app ha chiamato in una funzione generata dinamicamente. Un po 'come fanno oggi alcuni virus, immagino (ma non lo so davvero). Il risultato è stato un aumento di 100 volte delle prestazioni, il che ha reso davvero felici il cliente e il mio capo.

Il nuovo codice non era altrettanto gestibile, dato che avevo creato un compilatore personalizzato . Ma il vantaggio prestazionale ha superato di gran lunga lo svantaggio di manutenzione.


Più di recente stavo lavorando su un sistema che doveva analizzare dinamicamente una mosca XML. I file più grandi richiederebbero molto più tempo. Questo è stato molto sensibile alle prestazioni; un analisi troppo lenta renderebbe l'IU completamente inutilizzabile.

Questo genere di cose emerge continuamente.


Quindi .... a volte vuoi un codice gestibile e facile da scrivere. A volte vuoi un codice che venga eseguito rapidamente. Il compromesso è la decisione ingegneristica che devi prendere, su ogni progetto.


9
In tutti i tuoi esempi il costo per ottimizzarlo in seguito non è stato molto più elevato rispetto alla scrittura del codice veloce dall'inizio. Quindi scrivere prima un codice più lento e più semplice e poi ottimizzare se necessario ha funzionato bene in tutti.
CodesInChaos,

6
@CodeInChaos: la risposta non pretende diversamente. Parla alla domanda del PO "Perché dovrei preoccuparmi di micro prestazioni ed efficienza?" I problemi di pre-ottimizzazione sono stati semplicemente dedotti dagli altri risponditori.
webbiedave,

12

Se stai elaborando immagini di grandi dimensioni e iterando su ogni pixel, l'ottimizzazione delle prestazioni può essere fondamentale.


2
+1 - inoltre, finanziamenti ad alta frequenza, qualsiasi tipo di codificatore / decodificatore audio / video, simulazioni e modellistica (ad esempio giochi), bit a livello di sistema come programmatori di CPU e gestori di memoria, ecc.
Billy ONeal

3
PUO ' essere critico, ma E' critico solo dopo averlo dimostrato e averlo profilato dove pensi che sia il problema. (Suggerimento: probabilmente non c'è.)
SOLO IL MIO OPINIONE corretta,

2
@SOLO IL MIO OPINIONE corretta: in realtà, per l'elaborazione delle immagini, l'elaborazione dei dati è di solito il secondo consumatore di tempo (l'I / O è ancora il più grande). Tuttavia, l'ottimizzazione per l'I / O richiede molti progetti insoliti / folli e la loro accettazione da parte di colleghi programmatori, e talvolta è assolutamente impossibile migliorare. La parte di elaborazione, tuttavia, è generalmente parallelamente imbarazzante, quindi sono vantaggi facilmente ottenibili. (Una modifica può essere vista da un'altra come un'implementazione di un semplice manuale ... a meno che non si raggiunga il livello di VirtualDub)
rwong

12

Lascia che ti dica un po 'del perché dietro la cultura.

Se sei più vicino ai 40 che ai 20 e hai programmato da vivere attraverso i tuoi anni da adulti, allora sei diventato maggiorenne quando il C ++ era davvero l'unico gioco in città, le app desktop erano la norma e l'hardware era ancora software notevolmente in ritardo in termini di larghezza di banda / capacità di prestazione.

  • Prima dovevamo fare stupidi trucchi di programmazione per poter leggere file di grandi dimensioni (> 2G) ...
  • Ci preoccupavamo delle dimensioni eseguibili ...
  • Ci preoccupavamo della quantità di memoria consumata dai nostri programmi ...
  • Abbiamo preso regolarmente tempo algoritmico contro decisioni di compromesso spaziale
  • Anche sul back-end, abbiamo dovuto scrivere programmi CGI in C o C ++ affinché tutto gestisse un no decente. di RPS ... Era più veloce di molti ordini di grandezza .
  • Eseguivamo test sul merito delle prestazioni tra delphi / c ++ / vb!

Pochissime persone devono preoccuparsi di queste cose oggi.

Tuttavia, 10 anni fa dovevi ancora preoccuparti che il tuo software venisse scaricato su un modem a 56kb e funzionasse su un PC di 5 anni ... Ricordi quanto erano scadenti i PC nel 1996? Pensa in termini di 4 GB di disco rigido, un processore da 200 MHz e 128 Mb di RAM ...

E i server di 10 anni fa? Il server "next generation" di Dell costa $ 2000 e viene fornito con 2 (!) Processori Pentium da 1 Ghz, 2 Gb o Ram e un disco rigido da 20 Gb.

Era semplicemente un gioco di baseball diverso , e tutti quegli ingegneri "senior" che hanno 10 anni di esperienza (i ragazzi che probabilmente risponderanno alle tue domande), si sono tagliati i denti in quell'ambiente.


1
Gli ulteriori 20 anni di esperienza significano anche che abbiamo ottenuto i segni di bruciatura dopo aver attraversato il processo di ottimizzazione molte, molte volte ed evitare di fare cose che potrebbero averne bisogno in seguito. Stesso motivo per cui non colpisco il pollice (molto) mentre uso un martello.
Blrfl,

1
loop unfinding <shudder>
red-dirt

5
e oggi tutti i bambini che pensavano che larghezza di banda, CPU e memoria fossero illimitate stanno scoprendo che le loro applicazioni mobili non funzionano molto bene.
gbjbaanb,

9

ci sono già 10 risposte qui e alcune sono davvero buone, ma perché questa è una mia piccola pipì personale ...

l'ottimizzazione prematura che a) richiede molto più tempo rispetto a una soluzione semplice b) introduce più codice in cui la soluzione semplice sarebbe stata metà delle dimensioni e metà della complessità ec) rende le cose meno leggibili è ASSOLUTAMENTE da evitare. Tuttavia, se uno sviluppatore ha la possibilità di scegliere se utilizzare una std :: map o una std :: vector e sceglie la raccolta sbagliata per pura ignoranza per le prestazioni che sono altrettanto cattive se non peggiori dell'ottimizzazione prematura. E se potessi cambiare leggermente il tuo codice oggi, mantenere la leggibilità, mantenere la stessa complessità, ma renderlo più efficiente, lo faresti? O lo chiameresti "ottimizzazione prematura"? Trovo che molte persone non ci penserebbero nemmeno in un modo o nell'altro.

Una volta ero il tipo che consigliava la "micro-ottimizzazione" che richiedeva pochissime modifiche e mi è stata data la stessa risposta che hai appena detto: "non dovresti ottimizzare troppo presto. Facciamo in modo che funzioni e lo cambieremo più tardi se si verifica un problema di prestazioni ". Sono state necessarie diverse versioni prima di risolverlo. E sì, è stato un problema di prestazioni.

Mentre l'ottimizzazione precoce potrebbe non essere buona, penso che sia molto utile se le persone scrivono codice capendo cosa farà quel codice e non trascurano semplicemente qualsiasi domanda che si traduca nella notazione O (x) come "ottimizzazione". C'è un sacco di codice che puoi scrivere ora e con un piccolo pensiero sulle prestazioni evita l'80% dei problemi lungo la strada.

Considera anche che molti problemi di prestazioni non si verificano nel tuo ambiente e non subito. Alcune volte avrai un cliente che supera il limite o un altro sviluppatore decide di basarsi sul tuo framework e aumentare il numero di oggetti di 10 volte. Con alcuni per quanto riguarda le prestazioni ora, potresti evitare una riprogettazione molto costosa in seguito. E se il problema si presenta dopo il rilascio ufficiale del software, anche una semplice correzione diventa 20 volte più costosa da applicare.

Quindi, in conclusione, tenere sempre presente le prestazioni aiuta a sviluppare buone abitudini. Che sono altrettanto importanti avere un codice pulito, il più semplice possibile e organizzato.


+1: questo è uno dei fattori di occupabilità per coloro che sono assunti per sviluppare software e siti Web commerciali Shrinkwrap . La prevenzione è meno costosa della maledizione del cliente.
rwong,

6

Sospetto che molto di ciò che vedi sia un semplice errore di campionamento. Quando le persone hanno a che fare con situazioni semplici, scrivono codice e questa è la fine delle cose. Pongono domande quando hanno a che fare con qualcosa di relativamente complicato, come la necessità di ottimizzare, soprattutto in una situazione in cui non è necessariamente ovvio che sarebbe necessaria l'ottimizzazione.

Detto questo, c'è senza dubbio anche qualche ottimizzazione prematura. In modo corretto o meno, C e C ++ hanno una reputazione per le prestazioni, che tende ad attrarre le persone che si preoccupano delle prestazioni, comprese quelle che possono fare l'ottimizzazione tanto per il divertimento quanto perché è davvero necessario.


1
+1 - Nota: la maggior parte delle domande SO con il tag "performance" fanno probabilmente parte dell'errore di campionamento: P
Billy ONeal

3
Sicuramente vedo un sacco di domande di ottimizzazione prematura qui ... Penso che derivi dal fatto che molti programmatori hobbisti iniziano con l'idea di scrivere giochi, e c'è un enorme corpus di libri di "ottimizzazione" senza senso e siti web legati allo sviluppo di giochi che mettono cattive idee nella testa dei principianti. :-)
R ..

4
Quando hai a che fare con qualcosa di complicato, spesso sembra più facile prendersi una pausa dal problema delicato e sprecare il tuo tempo preoccupandoti se dovresti usare i++o++i
Carson63000,

@ Carson63000: sì, questo potrebbe distorcere totalmente i campioni. Oppure passano il tempo a rispondere alle domande sul perché la mia operator ++non è stata compilata.
rwong

4

Un paio di altre risposte menzionano i sistemi integrati e vorrei approfondire questo aspetto.

Esistono molti dispositivi contenenti processori di fascia bassa, ad esempio: il controller della caldaia in casa o un semplice calcolatore tascabile o le dozzine di chip all'interno di un'auto moderna.

Per risparmiare, questi possono avere quantità di flash (per memorizzare il codice) e RAM che sembrano minuscole a coloro che hanno scritto solo codice per PC o smartphone. Per risparmiare energia, possono funzionare a frequenze di clock relativamente basse.

Per fare un esempio, la famiglia di microcontrollori STM32 va da 24 MHz, 16 KB di flash e 4 KB di RAM , fino a 120 MHz, 1 MB di flash e 128 KB di RAM .

Quando si scrive codice per chip come questi, si risparmia molto tempo se si mira a rendere il codice il più efficiente possibile. Ovviamente, l'ottimizzazione prematura rimane una cattiva idea; ma con la pratica, impari come i problemi comuni possono essere risolti rapidamente e / o con risorse minime e il codice di conseguenza.


1
buoni punti da considerare per i sistemi embedded, un campo che lavoro in me stesso. Nonostante ciò, la mia esperienza nel corso degli anni è che l'ottimizzazione errata è sempre una perdita di tempo. Senza strumenti che ci guidano raramente troviamo le aree problematiche
Jeff,

2

Essendo linguaggi essenzialmente di basso livello, quando ci si imbatte in un caso di prestazioni patologiche in cui un dettaglio che non ha importanza il 99% delle volte sta causando il collo di bottiglia, si ha effettivamente l'opportunità di aggirare direttamente il problema (a differenza della maggior parte degli altri le lingue); ma ovviamente, spesso, come farlo nel modo più efficace non è immediatamente evidente. Da qui la metà delle strane / interessanti domande di micro-ottimizzazione poste qui.

L'altra metà proviene da quei curiosi su quanto possano avvicinarsi al metal. Essendo essenzialmente lingue di basso livello, dopo tutto ...


+1: vale la pena sottolineare che la "performance patologica" potrebbe accadere a chiunque nel mondo, indipendentemente dalla lingua o dalla piattaforma. La capacità di reimplementare in un linguaggio di livello inferiore per testare e leggere lo smontaggio può fornire ulteriori approfondimenti , ma non fornisce sempre una soluzione praticabile. Esempio: "So di poterlo fare in assembly - ma deve essere eseguito in un ambiente con attendibilità parziale!"
rwong

2

Le prestazioni sono sempre un argomento caldo quando si ha a che fare con C e C ++. Per quanto riguarda quanto lontano si dovrebbe andare, si può sempre impazzire fino al punto di inline ASM, o usando l'aritmetica del puntatore per una iterazione più veloce. Tuttavia, arriva un punto in cui si trascorre così tanto tempo a ottimizzare il fatto che il lavoro sullo sviluppo del programma complessivo si interrompe.

Quando si affrontano questi problemi, ci sono prestazioni del programmatore e prestazioni del codice. Su quali di questi concentrarsi porterai sempre domande interessanti. Alla fine la domanda più importante è quanto sia evidente per l'utente. L'utente lavorerà con i dati che creano array con centinaia o migliaia di elementi? In questo caso, la codifica per eseguire rapidamente le operazioni potrebbe indurre l'utente a lamentarsi che le operazioni standard del programma sono lente.

Quindi c'è l'utente che lavorerà con piccole quantità di dati. Alcuni file qua e là, dove fare cose come l'ordinamento e le operazioni sui file non sarà così evidente per l'utente se si utilizzano funzioni di livello superiore che rendono le cose più facili da mantenere a scapito di alcune prestazioni.

Questo è solo un piccolo esempio dei problemi che incontrerai. Altre questioni includono l'hardware dell'utente di destinazione. Dovrai preoccuparti molto di più delle prestazioni se hai a che fare con sistemi embedded, quindi se i tuoi utenti hanno, diciamo, macchine dual core con concerti di ram.


Hmm .. Non uso l'aritmetica del puntatore per un'iterazione più veloce - è una moltiplicazione e aggiungi istruzioni per ciclo sia che tu stia usando l'iterazione basata sull'indice che quella basata sul puntatore. Lo uso però, perché di solito è più chiaro dell'iterazione basata su indice.
Billy ONeal,

l'aritmetica del puntatore non è più veloce di w / e.

2

Perché ai programmatori importa così tanto? Ci sono idee sciocche che popolano le loro teste, come risolvere i problemi di prestazione prima che sappiano di averle e non capire quando stanno indovinando .

È complicato perché, nella mia esperienza, ci sono alcuni problemi di prestazioni che dovresti pensare in anticipo. Ci vuole esperienza per sapere cosa sono.

Detto questo, il metodo che uso è simile al tuo, ma non è lo stesso:

  1. Inizia con il design più semplice possibile. In particolare, la struttura dei dati dovrebbe essere il più normalizzata e minima possibile. Nella misura in cui ha inevitabilmente una ridondanza, si dovrebbe essere timidi di notifiche come un modo per mantenerlo coerente. È meglio tollerare un'incoerenza temporanea e ripararla con un processo periodico.

  2. Quando il programma è in fase di sviluppo, esegui periodicamente il tuning delle prestazioni, perché i problemi di performance hanno un modo di insinuarsi silenziosamente. Il metodo che uso è una pausa casuale , perché penso che sia il migliore.

Ecco un esempio di ciò che intendo.


1

Ad essere onesti, dipende da quale sia il tuo obiettivo e se stai programmando professionalmente o come hobby.

Oggi i computer moderni sono macchine davvero potenti. Indipendentemente dalle operazioni di base che decidi di fare, sia che tu stia tentando di ottimizzare o meno, possono velocizzare notevolmente il loro lavoro. Ma ovviamente, se stai facendo qualcos'altro (ad esempio, il supercalcolo per campi come la fisica o la chimica), potresti voler ottimizzare quanto vuoi.

I primi programmatori del MIT non erano nati per creare cose fantastiche; Hanno iniziato a semplificare e potenziare gli algoritmi esistenti. Il loro orgoglio era di fare in modo che 2 + 2 dessero quattro in due secondi in meno rispetto all'algoritmo esistente (questo è solo un esempio, hai capito). Hanno costantemente cercato di utilizzare meno schede perforate nelle loro macchine TI-83 per le prestazioni.

Inoltre, se stai programmando per sistemi embedded, allora devi sicuramente tenere d'occhio le micro prestazioni. Non vuoi avere un orologio digitale lento che ticchetta un secondo 5 nanosecondi prima di un altro orologio digitale.

Infine, se sei un programmatore di hobbisti, non c'è sicuramente nulla di male nell'ottimizzare i dettagli più piccoli, anche se il tuo programma è veloce. Non è necessario, ma sicuramente qualcosa su cui puoi lavorare e cogliere l'occasione per saperne di più. Se lavori in modo professionale su un software, non puoi concederti quel lusso se non è estremamente necessario.


1
Non penso che essere un programmatore hobbista abbia nulla a che fare con questo. Solo perché non stai facendo qualcosa di professionale non significa necessariamente che hai tutto il tempo da dedicare al mondo. Inoltre, la maggior parte degli hobbisti commetterà errori più grandi, come la scelta degli algoritmi sbagliati, rispetto alla maggior parte dei veri professionisti. Inoltre, il professionista sta probabilmente lavorando su un prodotto che elabora molti più dati rispetto all'hobbista (che quindi deve essere più veloce) e deve rendere i clienti soddisfatti delle prestazioni dell'app. Gli hobbisti non hanno tali vincoli.
Billy ONeal,

Non lo fanno, ma sicuramente hanno più tempo per lavorarci solo se lo desiderano.

3
Direi il contrario. Ho 8 ore o più al giorno per lavorare su qualcosa come un professionista. Ricevo 1, forse 2 ore al giorno per i miei progetti di hobby.
Billy ONeal,

1

usando un algoritmo O (N2) vs O (NlogN) in un elenco di 100 voci.

Sono stato in una situazione simile di recente. Avevo una serie di oggetti. Nel caso previsto, c'erano due (!) Voci nell'elenco e, anche nel caso peggiore, non mi aspetto più di quattro o forse otto.

Avevo bisogno di ordinare quell'elenco. Risulta, sostituendo std::sortcon una rete di smistamento (essenzialmente molti nidificati if) rasata una grande percentuale del tempo di esecuzione (non ricordo il numero ma era qualcosa di simile al 10-20%). Questo è un enorme vantaggio di una micro-ottimizzazione e il codice è assolutamente critico per le prestazioni.

Certo, l'ho fatto solo dopo aver profilato. Ma il punto è che se uso un linguaggio scomodo e contorto come il C ++ (per non parlare delle sue regole esageratamente complesse per la risoluzione del sovraccarico), allora voglio trarne tutti i benefici.


1

Consumo cumulativo di energia

C'è una risposta che penso sempre che manchi da queste discussioni e che mi preoccupa un po ': il consumo cumulativo di energia .

Certo, forse non importa molto se scrivi il tuo programma in un linguaggio interpretato di alto livello e lo lasci eseguire in un browser con un paio di livelli di riferimento indiretto, o se il tuo ciclo impiega 0,01 secondi invece di 0,001 secondi. Nessuno noterà, cioè nessun singolo utente noterà.

Ma quando decine di migliaia, o addirittura milioni di utenti in alcuni casi usano il tuo codice, aumenta tutta questa inefficienza. Se il tuo strumento impedisce a una CPU di entrare nello stato di sospensione per soli dieci secondi al giorno e un milione di utenti lo utilizza, il tuo algoritmo inefficiente consumava solo 140 kWh [1] in più al giorno.

Raramente lo vedo discusso e penso che sia triste. Sospetto fortemente che le cifre siano molto peggiori per i framework popolari, come Firefox e le fantasiose applicazioni web interattive, e sarebbe interessante ricercare.


[1] L'ho appena inventato, 10 milioni di secondi per 50 watt. La cifra esatta dipende da molte cose.


1
Dovresti iniziare menzionando la parola magica "mobile". Quando è in esecuzione su una piattaforma desktop, un'applicazione che impiega 1/100 di secondo del tempo della CPU per disegnare un frame 60 volte al secondo sarà "abbastanza veloce"; il miglioramento delle prestazioni di dieci volte farebbe la differenza zero per l'utente. Su una piattaforma mobile, tuttavia, un'applicazione che funziona al 90% di utilizzo della CPU può scaricare le batterie molto più velocemente rispetto al 10%.
supercat

1

A volte hai solo algoritmi che non possono essere migliori del tempo lineare per i quali c'è ancora una forte domanda di prestazioni.

Un esempio è l'elaborazione video in cui non è possibile rendere l'immagine / un fotogramma più luminoso come esempio di base senza scorrere tutti i pixel (beh, suppongo che tu possa con una sorta di struttura gerarchica che indica le proprietà ereditate dai bambini che alla fine scendono nelle tessere dell'immagine per i nodi foglia, ma poi rinvieresti un costo più elevato per il ciclo attraverso ogni pixel al renderer e il codice sarebbe probabilmente più difficile da mantenere rispetto al filtro immagine più micro-ottimizzato).

Ci sono molti casi del genere nel mio campo. Tendo a fare più cicli di complessità lineare che devono toccare tutto o leggere tutto di quelli che beneficiano di qualsiasi tipo di sofisticata struttura o algoritmo di dati. Non c'è lavoro che possa essere saltato quando tutto deve essere toccato. Quindi, a quel punto, se inevitabilmente hai a che fare con la complessità lineare, devi rendere il lavoro svolto per iterazione sempre più economico.

Quindi nel mio caso le ottimizzazioni più importanti e comuni sono spesso rappresentazioni di dati e layout di memoria, multithreading e SIMD (in genere in questo ordine con la rappresentazione dei dati è la più importante, poiché influisce sulla capacità di eseguire le ultime due). Non sto incontrando così tanti problemi che vengono risolti da alberi, tabelle hash, algoritmi di ordinamento e cose di quel tipo. Il mio codice giornaliero è più nella vena di "per ogni cosa, fai qualcosa".

Naturalmente è un altro caso di cui parlare quando sono necessarie ottimizzazioni (e soprattutto, quando non lo sono), micro o algoritmiche. Ma nel mio caso particolare, se un percorso di esecuzione critico necessita di ottimizzazione, i guadagni di velocità 10x + sono spesso raggiunti da ottimizzazioni a micro livello come multithreading, SIMD e riorganizzazione di layout di memoria e schemi di accesso per una migliore località di riferimento. Non è così spesso che riesco, ad esempio, a sostituire un ordinamento a bolle con un introsort o un ordinamento radix o un rilevamento di collisione con complessità quadratica con un BVH, piuttosto che trovare hotspot che, diciamo, traggono vantaggio dalla divisione del campo caldo / freddo.

Ora nel mio caso il mio campo è così critico per le prestazioni (raytracing, motori fisici, ecc.) Che un raytracer lento ma perfettamente corretto che impiega 10 ore per il rendering di un'immagine viene spesso considerato inutile o più di uno veloce che è completamente interattivo ma produce le immagini più brutte con raggi che perdono ovunque a causa della mancanza di un raggio a tenuta stagna / tri intersezione. La velocità è probabilmente la principale metrica di qualità di tale software, probabilmente anche più della correttezza fino ad un certo punto (poiché la "correttezza" è un'idea sfocata con raytracing poiché tutto è approssimativo, purché non si blocchi o qualcosa del genere). E in questo caso, se non penso all'efficienza in anticipo, trovo che devo effettivamente cambiare il codice al livello di progettazione più costoso per gestire progetti più efficienti. Quindi se non

Il gioco è un altro campo simile al mio. Non importa quanto sia corretta la logica del tuo gioco o quanto sia gestibile e brillantemente ingegnosa la tua base di codice se il tuo gioco gira a 1 frame al secondo come una presentazione. In alcuni campi la mancanza di velocità potrebbe effettivamente rendere l'applicazione inutile per i suoi utenti. A differenza dei giochi, non esiste una metrica "abbastanza buona" in aree come il raytracing. Gli utenti vogliono sempre più velocità e la concorrenza industriale è principalmente alla ricerca di soluzioni più veloci. Non sarà mai abbastanza buono fino a quando non sarà in tempo reale, a quel punto i giochi useranno i tracciatori di tracciati. E quindi probabilmente non sarà ancora abbastanza buono per VFX, da allora gli artisti potrebbero voler caricare miliardi di poligoni e simulazioni di particelle con auto-collisione tra miliardi di particelle a oltre 30 FPS.

Ora, se è di qualche conforto, nonostante ciò scrivo ancora circa il 90% del codice in un linguaggio di scripting (Lua) senza alcuna preoccupazione per le prestazioni. Ma ho una quantità insolitamente grande di codice che in realtà ha bisogno di scorrere da milioni a miliardi di cose, e quando fai scorrere da milioni a miliardi di cose, inizi a notare un'epica differenza tra ingenuo codice a thread singolo che invoca un errore cache con ogni iterazione rispetto a dire, codice vettoriale in esecuzione in parallelo accedendo a blocchi contigui in cui nessun dato irrilevante viene caricato in una riga della cache.


0

Come hai detto, la cura dei problemi di micro prestazioni è inutile prima di considerare alcuni problemi realmente causati da questi problemi


0

È davvero impossibile rispondere a questa domanda in generale. La maggior parte dei software che vengono costruiti oggi sono siti Web interni e applicazioni LOB, e per quel tipo di programmazione il tuo ragionamento è abbastanza corretto. D'altra parte, se stai scrivendo qualcosa come un driver di dispositivo o un motore di gioco, nessuna ottimizzazione è "prematura"; è probabile che il tuo software funzionerà su sistemi molto diversi con vincoli hardware diversi. In tal caso, dovresti progettare per le prestazioni e assicurarti di non scegliere un algoritmo non ottimale.


Esattamente quello che volevo dire. Ogni parte del software ha il suo dominio di applicazione e non ci si dovrebbe aspettare che si comporti in modo ottimale al di fuori di esso. In questo senso, l'ottimizzazione prematura è un esempio di perfezionismo fuorviato.
K.Steff,

0

Penso che il problema del programmatore, che si preoccupa così tanto delle prestazioni, sia che a volte, nella sua vita, aveva bisogno di scrivere un codice micro-performante, forse molto urgentemente, e ha imparato, imparato, imparato e alla fine sapeva molte cose e trucchi.

E ora è difficile dimenticare, e senza una misurazione preventiva, il che dimostra che non ha bisogno di preoccuparsi, è sicuro, usando il codice veloce.

È sempre bello mostrare la tua profonda conoscenza, le tue abilità e alcuni trucchi e riutilizzare qualcosa che hai imparato. Ti fa sentire prezioso e il tempo speso per l'apprendimento, vale la pena.

A volte nella mia vita, ho imparato che l'incremento del prefisso è più veloce ...

for (int i = 0; i < MAX; ++i)

... rispetto all'incremento postfisso:

for (int i = 0; i < MAX; i++)

Ora, se MAX è basso, non importerà, e se c'è un vero lavoro nel loop, non importerà anche. Ma non c'è motivo di usare la versione postfix, anche se il compilatore di oggi ottimizza il codice da solo.

Forse i cercatori di prestazioni hanno bisogno di un obiettivo aggiuntivo, oltre a scrivere "codice funzionante", come "codice funzionante e leggibile" per avere una linea guida nel mare di opzioni.


0

sono stato abbastanza fortunato da non dovermi preoccupare troppo o sono un cattivo programmatore?

Ti interessano le tue esigenze? Se le prestazioni non sono un requisito, non preoccuparti. Trascorrere del tempo significativo è un disservizio per il tuo datore di lavoro.

In una certa misura, le prestazioni sono sempre un requisito. Se riesci a colpirlo senza pensarci, sei giustificato a non pensarci.

Personalmente, sono spesso guidato dalle prestazioni quando i miei test impiegano troppo tempo per passare. Sono troppo impaziente di aspettare 5 minuti mentre passano una serie di test. Ma questo di solito è risolto armeggiando con i test.

La mia domanda è: perché a così tanti programmatori importa così tanto? È davvero un problema per la maggior parte degli sviluppatori,

Ci sono molti programmatori che sono giustificati in quanto a loro importa. Ci sono grandi numeri che non lo sono. Parliamo di quelli che non lo sono.

Una delle prime cose che i programmatori imparano a scuola, dopo come far funzionare davvero le cose, è la notazione O grande. Molti di loro apprendono correttamente la lezione e quindi si concentrano adeguatamente su cose drammaticamente influenzate da n. Altri non ottengono la matematica e tolgono solo la lezione che una volta che funziona deve essere veloce. Peggio ancora, alcuni di questi studenti non imparano mai nient'altro su ciò che è importante fare con il tuo codice oltre a farlo funzionare e farlo funzionare velocemente. Le lezioni perse: renderlo leggibile, progettalo bene, non giocarci senza motivo.

Knuth aveva ragione: l'ottimizzazione prematura è la radice di tutti i mali. Ma una volta che funziona qual è il prossimo passo? Veloce vero? NO! Il passaggio successivo è leggibile. Leggibile è il primo, il prossimo, il medio e l'ultimo passaggio. Molte delle persone che trovo a fare ottimizzazioni delle prestazioni non necessarie gettano leggibilità sotto il bus.

Alcuni provano persino un brivido perverso per quanto illeggibile sia il loro codice. Hanno dovuto soffrire guardando il codice difficile da capire creato da altri, quindi ora è il loro turno di rimborso.

Lo so perché ero solito farlo. Una volta ho refactored una linea di 5 righe perfettamente leggibile se struttura fino a un'espressione booleana indecifrabile di una riga e l'ho inviata con orgoglio al mio professore aspettandomi di impressionare dal momento che avrei potuto creare qualcosa di così compatto e intimidatorio. Non ho ricevuto gli elogi che speravo.

Se il codice rimane leggibile, renderlo più veloce in seguito è facile. Ecco perché Knuth sottolinea "prematuro" non "non necessario". Perché certo, più veloce è meglio. Ma meglio è solo meglio a seconda di ciò che sacrifichi per questo. Quindi aspetta di sapere di quali prestazioni hai veramente bisogno prima di fare sacrifici per questo. Sacrifica la leggibilità con riluttanza perché una volta sparita, è difficile tornare indietro.

Oltre la leggibilità c'è l'intero mondo della progettazione del software. Di cosa tratta questo sito. Alcuni non hanno idea di cosa fare per quanto riguarda il design. Quindi dal momento che non riescono a stupire con il design, creano un casino indecifrabile in modo che le persone non possano dire di non avere idea. Dal momento che nessuno corregge mai il proprio codice, deve essere un buon codice giusto?

Per alcuni, le prestazioni sono la scusa di tutte le scuse per fare quello che vogliono. I programmatori hanno molta potenza e autonomia. La fiducia è stata messa in loro. Non abusare della fiducia.

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.