Pulisci codice leggibile e veloce da leggere codice. Quando tagliare la linea?

66

Quando scrivo codice cerco sempre di rendere il mio codice il più pulito e leggibile possibile.

Ogni tanto arriva un momento in cui devi attraversare la linea e passare dal bel codice pulito al codice leggermente più brutto per renderlo più veloce.

Quando è OK attraversare quella linea?

    
posta Ken Cochrane 05.07.2011 - 03:28
fonte

10 risposte

116

Si attraversa la linea quando

  • Hai misurato che il tuo codice è troppo lento per l'uso previsto .
  • Hai provato dei miglioramenti alternativi che non richiedono il caricamento del codice.

Ecco un esempio del mondo reale: un sistema sperimentale che sto eseguendo stava producendo dati troppo lentamente, impiegando oltre 9 ore per esecuzione e usando solo il 40% della CPU. Invece di rovinare troppo il codice, ho spostato tutti i file temporanei su un filesystem in memoria. Aggiunte 8 nuove linee di codice non-brutto, e ora l'utilizzo della CPU è superiore al 98%. Problema risolto; nessuna bruttezza richiesta.

    
risposta data 05.07.2011 - 04:14
fonte
57

È una falsa dicotomia. Puoi rendere il codice e di facile manutenzione facile.

Il modo in cui lo fai è scrivere in modo pulito, soprattutto con una struttura dati il più semplice possibile.

Poi scopri dove sono i tempi di scarico (eseguendolo, dopo hai scritto, non prima), e correggili uno per uno. (Ecco un esempio.)

Aggiunto: Abbiamo sempre sentito parlare dei compromessi, giusto, come un compromesso tra tempo e memoria, o un compromesso tra velocità e manutenibilità? Sebbene tali curve possano benissimo esistere, non si deve presumere che ogni dato programma sia sulla curva , o anche in un punto qualsiasi vicino ad esso.

Qualsiasi programma che si trova sulla curva può facilmente (assegnandolo a un certo tipo di programmatore) essere reso sia molto più lento, e molto meno mantenibile, e quindi non sarà affatto vicino alla curva. Un programma di questo tipo ha quindi molto spazio per essere reso più veloce e più gestibile.

Nella mia esperienza, è qui che iniziano molti programmi.

    
risposta data 05.07.2011 - 04:22
fonte
31

Nella mia esistenza di OSS faccio un sacco di lavoro di biblioteca finalizzato alla performance, che è profondamente legato alla struttura dei dati del chiamante (cioè esterna alla libreria), con (in base alla progettazione) nessun mandato sui tipi in arrivo. Qui, il modo migliore per rendere questo performant è la meta-programmazione, che (dato che sono in .NET-land) significa IL-emit. Questo è un brutto codice, ma molto veloce.

In questo modo, accetto volentieri che il codice libreria sia "più brutto" del codice applicazione , semplicemente perché ha meno (o forse no ) controllo sugli input , quindi è necessario eseguire alcune attività attraverso diversi meccanismi. O come l'ho espresso l'altro giorno:

"coding over the cliff of insanity, so you don't have to "

Ora il codice applicazione è leggermente diverso, poiché è qui che gli sviluppatori "normali" (di buon livello) investono in genere molto del loro tempo collaborativo / professionale; gli obiettivi e le aspettative di ciascuno sono (IMO) leggermente diversi.

IMO, le risposte precedenti che suggeriscono che può essere e facili da mantenere si riferiscono al codice applicazione in cui lo sviluppatore ha più controllo sulle strutture di dati, e non sta usando strumenti come la meta-programmazione. Detto questo, ci sono diversi modi di fare la meta-programmazione, con diversi livelli di follia e diversi livelli di overhead. Anche in quell'arena devi scegliere il livello appropriato di astrazione. Ma quando attivamente, positivamente e sinceramente vuoi che gestisca dati inattesi nel modo più veloce assoluto; potrebbe essere brutto. Affrontalo; p

    
risposta data 05.07.2011 - 08:12
fonte
26

Quando hai profilato il codice e verificato che sta effettivamente causando un rallentamento significativo.

    
risposta data 05.07.2011 - 03:30
fonte
13

Il codice pulito non è necessariamente esclusivo con codice ad esecuzione rapida. Il codice normalmente difficile da leggere è stato scritto perché è stato più veloce da scrivere, non perché viene eseguito più velocemente.

Scrivere un codice "sporco" nel tentativo di renderlo più veloce è probabilmente poco saggio, dal momento che non si è certi che le modifiche apportino effettivamente qualcosa. Knuth ha detto meglio:

"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified."

In altre parole, scrivi prima il codice clean. Quindi, traccia il profilo del programma risultante e verifica se tale segmento è, in effetti, un collo di bottiglia delle prestazioni. In tal caso, ottimizza la sezione secondo necessità e assicurati di includere numerosi commenti sulla documentazione (possibilmente includendo il codice originale) per spiegare le ottimizzazioni. Quindi profila il risultato per verificare che tu abbia effettivamente apportato un miglioramento.

    
risposta data 05.07.2011 - 09:19
fonte
10

Poiché la domanda dice "veloce difficile da leggere codice", la risposta semplice non è mai. Non c'è mai una scusa per scrivere codice difficile da leggere. Perché? Due ragioni.

  1. Che succede se sei investito da un autobus sulla via di casa stasera? O (più ottimisticamente e più tipicamente) tolto questo progetto e riassegnato a qualcos'altro? Il piccolo vantaggio che immagini con il tuo groviglio di codice è completamente superato dal fatto che nessun altro può capirlo . Il rischio che questo comporta per i progetti software è difficile da sopravvalutare. Ho lavorato una volta con un importante PBX produttore (se lavori in un ufficio probabilmente hai uno dei loro telefoni sul scrivania). Il loro project manager mi disse un giorno che il loro prodotto principale - il software proprietario che trasformava una scatola Linux standard in una centralina telefonica completamente funzionante - era conosciuto all'interno dell'azienda come "il blob". Nessuno lo ha capito più. Ogni volta che hanno implementato una nuova funzionalità. avevano colpito compilando, poi si tiravano indietro, chiudevano gli occhi, contano fino a venti, poi sbirciano tra le dita per vedere se ha funzionato. Nessuna azienda ha bisogno di un prodotto principale che non controlla più, ma è uno scenario spaventosamente comune.
  2. Ma ho bisogno di ottimizzare! OK, quindi hai seguito tutti i consigli eccellenti in altre risposte a questa domanda: il tuo codice sta fallendo i suoi casi di test delle prestazioni, lo hai profilato con cura, identificato i colli di bottiglia , vieni con una soluzione ... e coinvolgerà alcuni bit-twiddling . Bene: ora vai avanti e ottimizza. Ma ecco il segreto (e potresti voler sederti per questo): l'ottimizzazione e la riduzione delle dimensioni del codice sorgente non sono la stessa cosa . Commenti, spazio bianco, parentesi quadre e nomi di variabili significative sono tutti aiuti enormi alla leggibilità che non ti costano assolutamente nulla perché il compilatore li getterà via. (O se stai scrivendo un linguaggio non compilato come JavaScript - e sì, ci sono ragioni molto valide per ottimizzare JavaScript - possono essere gestiti da un compressor .) Lunghe linee di codice minimalista e ristretto (come quello che muntoo ha pubblicato qui ) non hanno nulla a che fare con l'ottimizzazione: questo è un programmatore che cerca di mostrare quanto siano intelligenti sono imballando più codice possibile nel minor numero di caratteri possibile. Non è intelligente, è stupido. Un programmatore davvero intelligente è colui che può comunicare chiaramente le proprie idee agli altri.
risposta data 05.07.2011 - 09:18
fonte
4

Quando si tratta di codice throw-away. Intendo letteralmente: quando scrivi una sceneggiatura per eseguire un calcolo o un'attività una tantum e sai con tale certezza che non dovrai mai più eseguire quell'azione che puoi "rm file sorgente" senza esitazione, allora puoi scegliere il percorso brutto.

Altrimenti è una falsa dicotomia - se pensi di aver bisogno di renderlo brutto per farlo più velocemente, lo stai facendo male. (O i tuoi principi su ciò che è buono codice bisogno di revisione.Utilizzando goto è in effetti abbastanza elegante quando è la soluzione adeguata al problema. Raramente è tuttavia.)

    
risposta data 05.07.2011 - 06:49
fonte
3

Ogni volta che il costo stimato delle prestazioni inferiori sul mercato è superiore al costo stimato di manutenzione del codice per il modulo di codice in questione.

Le persone continuano a girare a mano con codice a mano SSE / NEON / ecc. assemblaggio per provare a battere il software di alcuni concorrenti sul popolare chip della CPU di quest'anno.

    
risposta data 05.07.2011 - 04:50
fonte
3

Non dimenticare che puoi rendere facile la comprensione del codice difficile da leggere con la documentazione e i commenti appropriati.

In generale, profilo dopo aver scritto un codice di facile lettura che esegue la funzione desiderata. I colli di bottiglia potrebbero richiedere che tu faccia qualcosa che lo faccia sembrare più complicato, ma lo risolvi spiegando te stesso.

    
risposta data 05.07.2011 - 10:59
fonte
0

Per me è una proporzione di stabilità (come nel cemento cementato, argilla cotta nel forno, incastonata nella pietra, scritta con inchiostro permanente). Più il tuo codice è instabile, più alta è la probabilità che tu debba cambiarla in futuro, più facilmente sarà flessibile, come l'argilla bagnata, rimanere produttivi. Sottolineo anche la flessibilità e la non leggibilità. Per me la facilità di cambiare codice è più importante della facilità di leggerlo. Il codice può essere facile da leggere e un incubo da cambiare, e che uso è in grado di leggere e comprendere facilmente i dettagli di implementazione se sono un incubo da cambiare? A meno che non sia solo un esercizio accademico, in genere il punto di essere in grado di comprendere facilmente il codice in una base di codice di produzione è con l'intento di poterlo modificare più facilmente secondo necessità. Se è difficile cambiare, molti dei vantaggi della leggibilità vanno fuori dalla finestra. La leggibilità è generalmente utile solo nel contesto della flessibilità e la flessibilità è utile solo nel contesto dell'instabilità.

Naturalmente anche il codice più difficile da mantenere immaginabile, indipendentemente da quanto sia facile o difficile da leggere, non pone problemi se non c'è mai un motivo per cambiarlo, usarlo solo. Ed è possibile ottenere tale qualità, specialmente per i codici di sistema di basso livello in cui le prestazioni tendono spesso a contare di più. Ho ancora un codice C che uso regolarmente e che non è cambiato dalla fine degli anni '80. Non ha bisogno di cambiare da allora. Il codice è fugacemente, scritto nei giorni truculenti, e a malapena lo capisco. Eppure è ancora applicabile oggi, e non ho bisogno di capire la sua implementazione per trarne un uso completo.

Scrittura approfondita dei test è un modo per migliorare la stabilità. Un altro è il disaccoppiamento. Se il tuo codice non dipende da nient'altro, allora l'unica ragione per cui può cambiare è se, di per sé, debba cambiare. A volte una piccola quantità di duplicazione del codice può fungere da meccanismo di disaccoppiamento per migliorare drasticamente la stabilità in modo tale da renderlo un valido compromesso se, in cambio, si ottiene un codice che ora è completamente indipendente da qualsiasi altra cosa. Ora quel codice è invulnerabile ai cambiamenti nel mondo esterno. Nel frattempo il codice che dipende da 10 diverse librerie esterne ha 10 volte il motivo per cui dovrebbe cambiare in futuro.

Un'altra cosa utile in pratica è separare la tua libreria dalle parti instabili della tua base di codice, possibilmente anche costruirla separatamente, come potresti fare per le librerie di terze parti (che allo stesso modo sono pensate per essere usate, non modificate, almeno non dalla tua squadra). Solo quel tipo di organizzazione può impedire alle persone di interferire con esso.

Un altro è il minimalismo. Meno il tuo codice cerca di fare, più è probabile che possa fare ciò che fa bene. I progetti monolitici sono quasi permanentemente instabili, dal momento che sempre più funzionalità vengono aggiunte a loro, più incomplete sembrano.

La stabilità dovrebbe essere il tuo obiettivo principale ogni volta che miri a scrivere codice che sarà inevitabilmente difficile da modificare, come il codice SIMD in parallelo che è stato messo a punto in modo microscopico. Contrastate la difficoltà di mantenere il codice massimizzando la probabilità che non dovrete modificare il codice e quindi non dovrete mantenerlo in futuro. Ciò porta i costi di manutenzione a zero, indipendentemente dalla difficoltà di manutenzione del codice.

    
risposta data 08.12.2017 - 02:24
fonte