Come misurare la complessità in pratica nel tuo progetto software di grandi dimensioni?

11

Nell'università, durante i nostri corsi sugli algoritmi, apprendiamo come calcolare con precisione la complessità dei vari algoritmi semplici che vengono utilizzati nella pratica, come le tabelle hash o l'ordinamento rapido.

Ma ora in un grande progetto software, quando vogliamo renderlo più veloce, tutto ciò che facciamo è guardare i singoli pezzi - ci sono alcuni loop nidificati che possono essere sostituiti da una tabella hash più veloce, una ricerca lenta qui che può essere accelerato da una tecnica più elaborata, ma non calcoliamo mai la complessità della nostra intera pipeline.

C'è un modo per farlo? Oppure la gente in pratica fa affidamento solo su "localmente" utilizzando un algoritmo veloce, per rendere l'intera applicazione più veloce, invece di considerare globalmente l'applicazione nel suo complesso?

(Perché mi sembra non banale mostrare che se si accumula un gran numero di algoritmi che sono noti per essere molto veloci da soli, si finisce anche con un'applicazione veloce nel suo complesso.)

Te lo chiedo, perché ho il compito di velocizzare un grande progetto che qualcun altro ha scritto, dove molti algoritmi interagiscono e lavorano sui dati di input, quindi non mi è chiaro come l'impatto del fare su singolo algoritmo più veloce su tutta l'applicazione.

    
posta user7088941 24.05.2018 - 16:38
fonte

3 risposte

5

I progetti software di grandi dimensioni sono costituiti da molti componenti diversi e non tutti sono in genere un collo di bottiglia. Al contrario: per quasi tutti i programmi che ho visto nella mia vita in cui le basse prestazioni erano un problema, si applicava il principio di Pareto : altro oltre l'80% dei guadagni in termini di prestazioni può essere ottenuto ottimizzando meno del 20% del codice (in realtà, penso che i numeri fossero spesso più del 95% - 5%).

Quindi iniziare a guardare singoli pezzi è spesso l'approccio migliore. Questo è perché il profiling (come spiegato nella risposta di David Arno ) va bene, dal momento che ti aiuta a identificare il citato 5% del codice in cui l'ottimizzazione ti darà il "big bang for the buck". Ottimizzare "l'intera applicazione" comporta un certo rischio di overengineering e, se ottimizzi il 95% anche di un fattore 10, spesso non avrà alcun effetto misurabile. Si noti inoltre che la profilazione indica molto più di qualsiasi ipotetica stima della complessità dell'algoritmo, dal momento che un semplice algoritmo che richiede i passi O (N ^ 3) può essere ancora più veloce di un algoritmo complesso che richiede O (N log (N)) fino a N è abbastanza piccolo.

Dopo che la profilazione ha rivelato i punti caldi, è possibile ottimizzarli. Ovviamente, un "punto caldo" può essere più grande di una o due righe di codice, a volte è necessario sostituire un intero componente per renderlo più veloce, ma in genere sarà ancora una piccola parte della base di codice in un programma più ampio .

Tipiche tecniche di ottimizzazione includono

  • miglioramento dell'uso di algoritmi e strutture dati

  • messa a punto del precedente

  • micro-ottimizzazioni in alcuni punti caldi

  • ricodifica di sezioni critiche utilizzando il codice assembly o CUDA

Si noti che queste tecniche stanno lavorando su diversi livelli di astrazione, alcuni dei quali vedono un componente più "nel suo complesso" rispetto ad altri. Quindi dipende da cosa intendi per "tutto ciò che facciamo è guardare i singoli pezzi" - se hai solo micro-ottimizzazioni in mente, non sono d'accordo sul fatto che "noi" lavoriamo solo su questo. Ma se intendi applicare queste ottimizzazioni su larga scala a parti o componenti isolati, allora "noi" stiamo probabilmente lavorando sulle parti giuste e dovresti mettere in dubbio le tue aspettative.

    
risposta data 24.05.2018 - 21:24
fonte
12

Il modo standard, provato e testato è profilare il codice . Esegui l'analisi dinamica del sistema in esecuzione per misurare tempi, utilizzo della memoria, ecc. Quindi analizza i risultati per individuare i colli di bottiglia delle prestazioni.

Questi colli di bottiglia vengono quindi riscritti sperimentalmente e il risultato viene profilato nuovamente per determinare che è stato raggiunto un aumento della velocità, una riduzione dell'uso della memoria ecc. Questo processo viene poi ripetuto fino al raggiungimento di un guadagno prestazionale accettabile.

    
risposta data 24.05.2018 - 16:48
fonte
3

Anche se le altre risposte sono corrette e forniscono una guida, penso che manchino un passaggio. In un sistema complesso come quello con cui stai lavorando ora, capire i diversi componenti che compongono il sistema è la chiave per capire perché qualcosa è lento.

Il mio primo passo sarebbe quello di mettere le mani su un diagramma dettagliato dell'architettura o crearne uno io stesso. Scopri quali sono i passaggi necessari per i componenti del software e per quanto tempo ciascun passo richiede.

Inoltre, scopri come i componenti interagiscono tra loro. Questo può fare la differenza.

Ad esempio, ho visto il codice in C # in cui l'interfaccia tra due componenti stava passando un IEnumerable creato dal primo componente, che è stato quindi enumerato dal secondo componente. In C # ciò richiede il cambio di contesto, che può essere costoso in determinate circostanze. Risolvendolo non ha alcun impatto sull'algoritmo. Una semplice .ToList () si assicura che il risultato sia raccolto prima che il prossimo passo risolva questo problema.

Un'altra cosa da considerare è l'impatto sul sistema su cui si sta eseguendo il codice. Le interazioni hardware possono ovviamente essere un fattore nei sistemi complessi. Cerca Disco I / O, allocazioni di memoria di grandi dimensioni e I / O di rete. A volte questi possono essere risolti in modo più efficiente modificando il sistema o addirittura sostituendo l'hardware.

    
risposta data 25.05.2018 - 09:43
fonte

Leggi altre domande sui tag