Cosa rende lento un prodotto software ampio e complesso? [chiuso]


16

Per un motivo che è in gran parte irrilevante, ho installato Delphi 7 ancora una volta in così tanto tempo. Devo dire che sono stato completamente spazzato via - in un certo senso non lo sono da un po 'di tempo. Non è così che ricordo le cose. L'installazione ha richiesto circa 30 secondi. Il lancio ha richiesto 2 secondi ed è stato immediatamente utilizzabile. Posso premere "Esegui" il secondo dopo l'avvio, e meno di un secondo dopo il programma vuoto è già visibile e in esecuzione. Evviva i computer che stanno diventando molto più veloci!

Ma il motivo per cui sono stato spazzato via in questo modo è perché di solito uso Visual Studio 2010, che non mi sembra affatto scattante. Certo, Delphi 7 è un sistema molto più piccolo di Visual Studio 2010, ma ha l' aspetto di avere tutte le cose davvero necessarie lì: una tavolozza di controllo, un progettista di moduli, un editor di codice con completamento del codice. Mi rendo conto che il linguaggio potrebbe essere più semplice e che il completamento del codice potrebbe essere molto meno potente e l'IDE potrebbe non essere altrettanto estensibile e ricco di funzionalità, ma comunque: non capisco come (cioè attraverso quale meccanismo) abbia molte funzionalità extra (che forse non avrei ancora attivato) fanno sì che un sistema come Visual Studio sembri sempre lento al confronto.

Vorrei chiedere alle persone con esperienza nel lavorare con i sistemi la scala di Visual Studio: che cosa li rende lenti? Sono gli strati su strati di astrazioni necessari per mantenere la base di codice all'interno delle capacità di comprensione umana? È la pura quantità di codice che deve essere eseguita? È la tendenza moderna verso approcci che fanno risparmiare tempo al programmatore a spese (incredibilmente enormi) nel reparto cicli di clock / utilizzo della memoria?


7
Semplice: all'aumentare della massa, è necessaria più forza per superare l'inerzia.
Shog9,

Qualcuno una volta mi ha detto dei manager ma non ci credo affatto.
MIchael Grassman,

1
Questa è una grande parte del motivo per cui uso ancora principalmente D7 per la programmazione Delphi.
GrandmasterB,

Il codice più veloce è quello che non viene mai eseguito.
Henry,

4
@romkyns: Trovo che molti software nell'era moderna siano spesso incredibilmente gonfiati, inutilmente grandi e ingombranti. Molti software ora risolvono gli stessi problemi che sono stati risolti dieci, anche venti anni fa, con una frazione della potenza e dello spazio. Perché è ancora in ritardo quanto mai, se non di più? Inefficienza e gonfia.
Orbling

Risposte:


20

Astronautica architettonica

Visual Studio 2010 è basato su Windows Presentation Foundation. Dai un'occhiata alla classe Button per WPF. È il nono figlio di una classe base. Ha circa 5 pagine di proprietà, metodi ed eventi. Dietro le quinte ha altre cinque pagine di definizioni di stile che descrivono i suoi angoli meravigliosamente arrotondati e le sottili transizioni dell'animazione quando un cursore del mouse si sposta su di esso. Questo è tutto per qualcosa che fondamentalmente mostra del testo o un'immagine e produce un evento click quando rileva un pulsante del mouse che scende.

Arresta un programma come Visual Studio in qualsiasi punto casuale. Guarda la traccia dello stack. È molto probabile che tu abbia 20 livelli di profondità nello stack di chiamata e che cinque DLL siano state caricate per arrivarci.

Ora confronta queste due cose con Delphi. Scommetto che scopri che un pulsante Delphi ha solo 20 proprietà, metodi ed eventi. Scommetto che l'IDE Delphi ha solo una traccia dello stack di 5-7 livelli di profondità. Perché quando i computer erano più lenti, non potevi sopportare il sovraccarico di Visual Studio 2010 senza che l'IDE impiegasse 40 minuti per iniziare :-)

Uno è migliore dell'altro? Bene, in genere posso dire a un programma Delphi quando si carica perché sembra piatto, i colori sono disattivati ​​(forse 8 bit?) E non ci sono ombre o animazioni sottili. Mi sento solo "economico" in questi giorni. Economico, ma veloce.

Stiamo meglio? Questa è una domanda per i filosofi, non per i programmatori.


4
Un programma delphi non sembra piatto. Piuttosto, un programmatore programma un programma per apparire piatto. Con Delphi puoi creare interfacce moderne, a colori e dall'aspetto gradevole, proprio come in C # o C ++.
GrandmasterB,

2
Questa è una risposta perspicace; ma non sono sicuro che sia completo. Visual Studio 2008 (il predecessore del 2010) non contiene WPF ed è ancora più lento di Delphi 7. Continueresti a dire la stessa cosa sulla profondità dello stack delle chiamate e sul numero di DLL caricate?
Timwi,

3
@Timwi Sì, assolutamente. Il mio punto era meno sui mali di WPF (in realtà mi piace il WPF) e più su come tendiamo ad aggiungere strati su strati di astrazione software quando viene data la scelta. Forse Visual Studio 2008 non aveva abbastanza spese generali, ma come hai notato ne aveva abbastanza :-)
Jay Beavers,

@GrandmasterB, non sto sbattendo Delphi perché ha meno ipotesi e librerie più semplici. WPF è stato progettato supponendo che l'accelerazione hardware della GPU consentirebbe ai programmi di utilizzare colori più profondi, animazioni frequenti, fusione alfa, ombre, ecc. Delphi è stato progettato in un momento in cui non è stato possibile fare queste ipotesi. Potresti ri-implementare tutto questo in Delphi? Certo, ma dovresti inserire un sacco di codice solo per ottenere il comportamento di un pulsante WPF. Tra i lati positivi, un pulsante Delphi non presenta requisiti di CPU, memoria e GPU che un pulsante WPF ha sia quale fosse la domanda dell'OP @.
Jay Beavers,

10
Il tuo argomento per l'interfaccia utente piatta e semplice è completamente invalidato dalla nuova interfaccia utente "moderna" di Windows 10. Ora abbiamo tutto quel sovraccarico per creare pulsanti piatti, quadrati, semplici come 30 anni fa.
gbjbaanb,

11

Vorrei chiedere alle persone con esperienza nel lavorare con i sistemi la scala di Visual Studio: che cosa li rende lenti? Sono gli strati su strati di astrazioni necessari per mantenere la base di codice all'interno delle capacità di comprensione umana? È la pura quantità di codice che deve essere eseguita? È la tendenza moderna verso approcci che fanno risparmiare tempo al programmatore a spese (incredibilmente enormi) nel reparto cicli di clock / utilizzo della memoria?

Penso che tu ne abbia indovinato un certo numero, ma vorrei offrire quello che considero il fattore più importante, avendo lavorato su una base di codice ragionevolmente grande (non sono sicuro che sia grande come Visual Studio - era tra i milioni di righe di codice categoria e circa un migliaio di plugin) per circa 10 anni e si verificano fenomeni osservativi.

È anche un po 'meno controverso poiché non va nelle API o nelle funzionalità del linguaggio o cose del genere. Questi si riferiscono a "costi" che possono generare un dibattito piuttosto che a "spese", e voglio concentrarmi su "spese".

Coordinamento e legami allentati

Quello che ho osservato è che un coordinamento lento e una lunga eredità tendono a portare a molti rifiuti accumulati.

Ad esempio, ho trovato circa cento strutture di accelerazione in questa base di codice, molte delle quali ridondanti.

Avremmo come un albero KD per accelerare un motore fisico, un altro per un nuovo motore fisico che spesso correva in parallelo con quello vecchio, avremmo avuto dozzine di implementazioni di ocre per vari algoritmi di mesh, un altro albero KD per il rendering , raccolta, ecc. ecc. ecc. Queste sono tutte strutture di alberi grandi e voluminose utilizzate per accelerare le ricerche. Ogni singolo può portare da centinaia di megabyte a gigabyte di memoria per un input di dimensioni molto medie. Non sono sempre stati istanziati e utilizzati tutto il tempo, ma in qualsiasi momento, 4 o 5 potrebbero essere in memoria contemporaneamente.

Ora tutti questi stavano memorizzando gli stessi identici dati per accelerare le ricerche per loro. Puoi immaginarlo come il vecchio database analogico che memorizza tutti i suoi campi in 20 diverse mappe / dizionari / alberi B + ridondanti contemporaneamente, organizzati in modo identico con le stesse chiavi e li cerca continuamente. Ora stiamo prendendo 20 volte la memoria e l'elaborazione.

Inoltre, a causa della ridondanza, c'è poco tempo per ottimizzare uno di essi con il prezzo di manutenzione che ne deriva e, anche se lo facessimo, avrebbe solo il 5% dell'effetto che idealmente avrebbe.

Quali sono le cause di questi fenomeni? La coordinazione libera è stata la causa numero uno che ho visto. Molti membri del team lavorano spesso nei loro ecosistemi isolati, sviluppando o utilizzando strutture di dati di terze parti, ma non usando le stesse strutture che altri membri del team stavano usando anche se erano palesi duplicati delle stesse identiche preoccupazioni.

Cosa causa il persistere di questi fenomeni? L'eredità e la compatibilità sono state la causa numero uno che ho visto. Poiché abbiamo già pagato i costi per implementare queste strutture di dati e grandi quantità di codice dipendevano da queste soluzioni, spesso era troppo rischioso tentare di consolidarle in meno strutture di dati. Anche se molte di queste strutture dati erano altamente ridondanti concettualmente, non erano sempre vicine allo stesso identico nei loro design di interfaccia. Quindi sostituirli sarebbe stato un grande e rischioso cambiamento piuttosto che lasciarli consumare memoria e tempo di elaborazione.

Efficienza di memoria

In genere l'uso della memoria e la velocità tendono ad essere correlati almeno a livello di massa. Spesso è possibile individuare il software lento in base al modo in cui si sta eseguendo il backup della memoria. Non è sempre vero che più memoria porta a un rallentamento, poiché ciò che conta è la memoria "calda" (quale memoria si accede continuamente - se un programma utilizza un carico di memoria della barca ma solo 1 megabyte viene utilizzato tutto il tempo, quindi non è un grosso problema in termini di velocità).

Quindi puoi individuare i potenziali maiali in base all'uso della memoria per la maggior parte del tempo. Se un'applicazione richiede decine di centinaia di megabyte di memoria all'avvio, probabilmente non sarà molto efficiente. Decine di megabyte potrebbero sembrare piccole quando abbiamo gigabyte di DRAM in questi giorni, ma le cache della CPU più grandi e lente sono ancora nell'intervallo di megabyte miseri, e le più veloci sono ancora nell'intervallo dei kilobyte. Di conseguenza, un programma che utilizza 20 megabyte solo per avviarsi e non fare nulla in realtà sta ancora usando abbastanza "molta" memoria dal punto di vista della cache della CPU hardware, soprattutto se si accederà ripetutamente a tutti i 20 megabyte di tale memoria e frequentemente mentre il programma è in esecuzione.

Soluzione

Per me la soluzione è cercare team più coordinati e più piccoli per costruire prodotti, quelli che possono in qualche modo tenere traccia delle loro "spese" ed evitare di "acquistare" gli stessi articoli ancora e ancora e ancora.

Costo

Mi immergerò nella parte più controversa del "costo" solo un po 'da teenager con un fenomeno di "spesa" che ho osservato. Se un linguaggio finisce con un inevitabile prezzo per un oggetto (come uno che fornisce una riflessione sul runtime e non può forzare l'allocazione contigua per una serie di oggetti), quel prezzo è costoso solo nel contesto di un elemento molto granulare, come un singolo Pixelo Boolean.

Eppure vedo un sacco di codice sorgente per i programmi che gestiscono un carico pesante (es: gestire da centinaia di migliaia a milioni di Pixelo Booleancasi) pagando quel costo a un livello così granulare.

La programmazione orientata agli oggetti può in qualche modo esasperarla. Eppure non è il costo di "oggetti" di per sé o addirittura OOP in difetto, è semplicemente che tali costi vengono pagati a un livello così granulare di un elemento adolescente che verrà istanziato da milioni.

Quindi questo è l'altro fenomeno "costo" e "spesa" che sto osservando. Il costo è di pochi centesimi, ma si sommano se acquistiamo un milione di lattine di soda singolarmente invece di negoziare con un produttore per un acquisto all'ingrosso.

La soluzione qui per me è l'acquisto "all'ingrosso". Gli oggetti vanno benissimo anche nei linguaggi che hanno un prezzo di centesimi per ognuno a condizione che questo costo non venga pagato individualmente un milione di volte per l'equivalente analogico di una lattina di soda.

Ottimizzazione precoce

Non mi è mai piaciuta molto la formulazione usata da Knuth qui, perché "l'ottimizzazione prematura" raramente rende i programmi di produzione reali più veloci. Alcuni interpretano ciò come "ottimizzazione precoce" quando Knuth significava più "ottimizzazione senza la conoscenza / esperienza adeguata per conoscere il suo vero impatto sul software". Semmai, l'effetto pratico della vera ottimizzazione prematura spesso rallenta il software , poiché il degrado della manutenibilità significa che c'è poco tempo per ottimizzare i percorsi critici che contano davvero .

Questo è il fenomeno finale che ho osservato, in cui gli sviluppatori che cercavano di risparmiare qualche soldo sull'acquisto di una singola lattina di soda, mai più per essere comprati, o peggio ancora, una casa, stavano perdendo tutto il loro tempo a pizzicare penny (o peggio, penny immaginari da non riuscendo a capire il loro compilatore o l'architettura dell'hardware) quando miliardi di dollari furono spesi inutilmente altrove.

Il tempo è molto limitato, quindi cercare di ottimizzare gli assoluti senza avere le informazioni contestuali appropriate spesso ci priva dell'opportunità di ottimizzare i luoghi che contano davvero, e quindi, in termini di effetto pratico, direi che "l'ottimizzazione prematura rende il software molto più lento. "

Il problema è che ci sono tipi di sviluppatori che prenderanno ciò che ho scritto sopra sugli oggetti e cercheranno di stabilire uno standard di codifica che vieti la programmazione orientata agli oggetti o qualcosa di folle di quel tipo. Un'ottimizzazione efficace è un'efficace definizione delle priorità ed è assolutamente inutile se stiamo affogando in un mare di problemi di manutenzione.


2
Debito tecnico, in altre parole. Debito tecnico che non viene mai pagato.
Robert Harvey,

1
Robert ha ragione. Un errore da parte di un ragazzo, duecento errori --forceda parte di manager che urlavano "verrai licenziato se non lo implementerai entro domani" che spazzerà via anni di buone pratiche di ingegneria del software, TDD, unit test e qualsiasi principio di programmazione umana e sana , più altre due volte che eri stanco .. quel tizio che ha lasciato la compagnia pazzo perché è stato licenziato senza motivo e ha incasinato la base di codici .. quelle librerie fuori produzione che non hai mai aggiornato ... e qui ce l'hai: deliziosi spaghetti codebase e software gonfio. Buon
appetito

2
Interessante, soprattutto per come hai visto un'eccessiva granularità abusata. Mi sono sorpreso a fare qualcosa di simile in occasioni in passato e di conseguenza ho avuto scarse prestazioni. Questo è abbastanza simile alla tua risposta di qualche giorno fa sull'utilizzo di raccolte e algoritmi di massa in preferenza sull'eccessiva granularità . Non posso credere che la risposta non sia stata più apprezzata per la sua profondità. Mi fa ripensare a molti dei progetti che ho realizzato nel corso degli anni. Mi chiedo perché quelle tecniche non siano più ampiamente promosse?
Mike supporta Monica il

2
@ Mike Sono un po 'un disco rotto quando si tratta di cercare di promuovere una mentalità più orientata ai dati. È popolare nel settore dei giochi in cui stanno cercando di utilizzare ogni centimetro dell'hardware. Detto questo, è certo che riduce la flessibilità. Se hai una classe di pixel astratta, puoi fare cose folli con quella come avere una singola immagine che mescola due o più formati di pixel diversi! Tuttavia, quando abbiamo a che fare con percorsi critici, probabilmente nessuna immagine trarrebbe beneficio da quel livello di flessibilità e le prestazioni iniziano a diventare una vera preoccupazione per qualsiasi cosa che coinvolga immagini e pixel.

1
Ai vecchi tempi ho implementato del codice per bypassare le API grafiche e accedere direttamente ai pixel in memoria per un pezzo critico del mio codice. La differenza tra i molti strati di astrazione e accesso diretto era qualcosa come 100x, che contava su un computer in quei giorni. Ora i tuoi computer sono abbastanza veloci da poter scorrere qualsiasi quantità di astrazione, se necessario.
Michael Shopsin
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.