La programmazione modulare influisce sui tempi di calcolo?


19

Tutti dicono che dovrei rendere il mio codice modulare, ma non è meno efficiente se uso più chiamate di metodo piuttosto che meno metodi, ma più grandi? Qual è la differenza in Java, C o C ++ per quella materia?

Capisco che è più facile modificarlo, leggerlo e comprenderlo, specialmente in un gruppo. Quindi la perdita di tempo di calcolo è insignificante rispetto ai benefici di ordinazione del codice?


2
La domanda è: quanto tempo impiegherà il tempo di elaborazione risparmiato a passare il tempo impiegato per la manutenzione più difficile. La risposta dipende interamente dalla tua domanda.
Blrfl,

2
Molte buone domande generano un certo grado di opinione basato sull'esperienza degli esperti, ma le risposte a questa domanda tenderanno ad essere quasi interamente basate su opinioni, piuttosto che su fatti, riferimenti o competenze specifiche.
moscerino del

10
Vale anche la pena sottolineare che la penalità di calcolo per una chiamata di funzione o metodo è così minuscola che anche in un programma molto ampio con molte funzioni e chiamate di metodo, effettuare tali chiamate non si classifica nemmeno sul grafico.
Greyfade,

1
@greyfade: questo è vero per i salti diretti, ma un ulteriore salto previsto indiretto potrebbe costare ad esempio ~ 3% del tempo di esecuzione totale del programma (solo un numero dal programma che ho verificato di recente - potrebbe non essere stato rappresentativo però). A seconda della tua area potresti considerarla o meno significativa ma è stata registrata sulla carta (e ovviamente è almeno parzialmente ortogonale alla modularità).
Maciej Piechotka,

4
L'ottimizzazione prematura è la radice di tutti i mali. Il codice lineare è leggermente più veloce del codice modulare. Il codice modulare è molto più veloce del codice spaghetti. Se miri a un codice lineare senza un progetto (MOLTO) completo dell'intera faccenda, finirai con un codice spaghetti, lo garantisco.
SF.

Risposte:


46

Sì, è irrilevante.

I computer sono motori di esecuzione instancabili e quasi perfetti che funzionano a velocità totalmente non comparabili ai cervelli. Mentre c'è una quantità misurabile di tempo che una chiamata di funzione aggiunge al tempo di esecuzione di un programma, questo è nulla in confronto al tempo aggiuntivo necessario al cervello della persona successiva coinvolta nel codice quando devono districare la routine illeggibile persino per cominciare a capire come lavorarci. Puoi provare il calcolo per uno scherzo: supponi che il tuo codice debba essere mantenuto solo una volta e aggiunge solo mezz'ora al tempo necessario affinché qualcuno faccia i conti con il codice. Prendi la velocità di clock del tuo processore e calcola: quante volte il codice dovrebbe essere eseguito anche solo per sognare di compensarlo?

In breve, avere pietà della CPU è completamente, completamente fuorviato il 99,99% delle volte. Per i rari casi rimanenti, utilizzare i profiler. Non Non supporre che è possibile individuare questi casi - non si può.


2
Mentre sono d'accordo sul fatto che la maggior parte delle volte si tratta di ottimizzazione prematura, penso che il tuo argomento con il tempo sia negativo. Il tempo è relativo in diverse situazioni e non puoi semplicemente calcolare come hai fatto tu.
Honza Brabec,

28
+1 solo per l'espressione "avere pietà della CPU", perché accentua così bene la testa sbagliata nell'ottimizzazione prematura.
Michael Borgwardt,

4
Molto correlati: quanto sono veloci i computer in termini quotidiani? Fare di tutto per evitare alcune chiamate di funzione per "velocità" è come fare di tutto per salvare qualcuno un minuto in totale nel corso della sua intera vita . In breve: non vale nemmeno il tempo che ci vuole per considerare.
BlueRaja - Danny Pflughoeft,

7
+1 per vendere in modo così eloquente ciò che manca a molti, ma hai dimenticato di aggiungere il peso più importante alla bilancia che rende ancora più grave la compassione della CPU: ignorare i soldi persi e il tempo speso per quella manutenzione unica; se impiegasse 1 secondo, c'è ancora un lavandino molto più insidioso lì. Rischio di bug . Più è complicato e difficile per quel manutentore successivo cambiare il codice, maggiore è il rischio che implementi bug al suo interno, il che può causare costi inevitabilmente insormontabili da rischi per gli utenti e qualsiasi bug causa manutenzione successiva (che può causare bug ...)
Jimmy Hoffa,

Un mio vecchio insegnante diceva "Se stai pensando se stai ottimizzando prematuramente o no, fermati proprio qui. Se questa fosse la scelta giusta, lo sapresti."
Ven

22

Dipende.

Nel mondo glacialmente lento che è la programmazione Web, dove tutto accade a velocità umane, la programmazione pesante di metodo, in cui il costo della chiamata al metodo è paragonabile o supera il costo dell'elaborazione effettuata dal metodo, probabilmente non importa .

Nel mondo della programmazione di sistemi embedded e dei gestori di interrupt per gli interrupt ad alta velocità, è certamente importante. In quell'ambiente, i soliti modelli di "accesso alla memoria sono economici" e "il processore è infinitamente veloce" si guastano. Ho visto cosa succede quando un programmatore orientato agli oggetti mainframe scrive il suo primo gestore di interrupt ad alta velocità. Non è stato carino.

Diversi anni fa, stavo realizzando la colorazione blob di connettività a 8 vie non ricorsiva su immagini FLIR in tempo reale, su quello che a quel tempo era un processore decente. Il primo tentativo ha utilizzato una chiamata di subroutine e l'overhead della chiamata di subroutine ha reso il processore attivo. (4 chiamate PER PIXEL x 64 K pixel per fotogramma x 30 fotogrammi al secondo = lo capisci). Il secondo tentativo ha cambiato la subroutine in una macro C, senza perdita di leggibilità, e tutto era rose.

Devi guardare DURO quello che stai facendo e l'ambiente in cui lo farai.


Ricorda che la maggior parte dei problemi di programmazione moderni viene gestita al meglio con un sacco di codice orientato agli oggetti e favorevole ai metodi. Saprai quando ti trovi in ​​un ambiente di sistemi embedded.
Kevin - Ripristina Monica il

4
+1, ma con un avvertimento: di solito è un caso che un compilatore ottimizzatore farà spesso un lavoro migliore di inline, srotolamento in loop e ottimizzazione scalare e vettoriale di una persona. Il programmatore deve ancora conoscere molto bene la lingua, il compilatore e la macchina per trarne vantaggio, quindi non è magico.
detly

2
È vero, ma hai ottimizzato il codice dopo aver scritto la logica e profilato per trovare la parte problematica nel tuo algoritmo. Non hai iniziato a programmare per prestazioni ma per leggibilità. E le macro ti aiuteranno a mantenere il tuo codice leggibile.
Uwe Plonus,

2
Anche nella programmazione di sistemi embedded, inizia scrivendo il codice corretto e inizia a ottimizzare solo dopo ciò, se necessario e come guidato dalla profilazione . Altrimenti è troppo facile dedicarsi a molto lavoro che non ha alcun effetto reale sulle prestazioni / dimensioni del codice e che rende la fonte difficile da capire.
Donal Fellows,

1
@detly: Sì e no. I compilatori moderni in generale possono fare un lavoro migliore ENTRO I LIMITI IMPOSTATI DALLA LINGUA. Il programmatore umano sa se i limiti imposti dalla lingua sono applicabili nel caso specifico in questione. Ad esempio, i compilatori Cn e C ++ sono richiesti dagli standard linguistici per consentire al programmatore di fare determinate cose molto patologiche e generare codice che funzioni comunque. Il programmatore può sapere che non è abbastanza pazzo, stupido o avventuroso da fare quelle cose e fare ottimizzazioni che altrimenti non sarebbero sicure, perché sa che in questo caso sono al sicuro.
John R. Strohm,

11

Prima di tutto: i programmi in una lingua superiore devono essere letti dagli umani e non dalle macchine.

Quindi scrivi i programmi in modo che tu li capisca. Non pensare alle prestazioni (se hai seriamente problemi di prestazioni, allora profila la tua applicazione e migliora le prestazioni dove è necessario).

Anche se è vero che chiamare un metodo o una funzione richiede un certo sovraccarico, questo non ha importanza. Oggi i compilatori dovrebbero essere in grado di compilare il codice in un linguaggio macchina efficiente in modo che il codice generato sia efficiente per l'architettura di destinazione. Usa gli switch di ottimizzazione del tuo compilatore per ottenere il codice efficiente.


5
Direi che dovremmo scrivere programmi in modo tale che gli altri li capiscano.
Bartlomiej Lewandowski il

La prima persona che deve leggere un programma è lo sviluppatore stesso. Questo è il motivo per cui ho scritto voi . Se anche altre persone possono leggere il programma è bello ma (ai miei occhi) non necessario (in primo luogo). Se lavori in gruppo, anche altre persone dovrebbero capire il programma, ma la mia intenzione era quella persona che doveva leggere un programma, non i computer.
Uwe Plonus,

5

In genere, quando altrimenti avresti una funzione di grandi dimensioni e la dividi in molte altre più piccole, queste più piccole verranno evidenziate perché l'unico aspetto negativo dell'inline (ripetere troppo le stesse istruzioni) non è rilevante in questo caso. Ciò significa che il tuo codice funzionerà come se avessi scritto una grande funzione.

Se non sono allineati per qualche motivo e questo diventa un problema di prestazioni, allora dovresti considerare l'allineamento manuale. Non tutte le applicazioni sono moduli CRUD collegati in rete con latenze intrinseche enormi.


2

Probabilmente non ci sono costi di calcolo. Di solito i compilatori / JIT negli ultimi 10-20 anni o giù di lì hanno a che fare con la funzione che si allinea perfettamente. Per il C / C ++ di solito è limitato a funzioni 'inlinabili' (ovvero la definizione di funzione è disponibile per il compilatore durante la compilazione - ovvero è nell'intestazione dello stesso file) ma le attuali tecniche di LTO lo superano.

Se dovresti dedicare tempo all'ottimizzazione dipende dall'area su cui stai lavorando. Se hai a che fare con un'applicazione "normale" che ha trascorso la maggior parte del tempo in attesa di input, probabilmente non dovresti preoccuparti delle ottimizzazioni a meno che l'applicazione non senta "lenta".

Anche in questi casi dovresti concentrarti su molte cose prima di fare la micro-ottimizzazione:

  • Dove sono i problemi? Di solito le persone hanno difficoltà a trovare gli hotspot mentre leggiamo il codice sorgente in modo diverso. Abbiamo un diverso rapporto di tempo operativo e li eseguiamo in sequenza, mentre i moderni processori no .
  • Fare qualche calcolo è necessario ogni volta? Ad esempio, se si modifica un singolo parametro su migliaia, è possibile che si desideri calcolare solo una parte interessata anziché l'intero modello.
  • Usi un algoritmo ottimale? Il passaggio da O(n)a O(log n)potrebbe avere un impatto molto maggiore di qualsiasi cosa tu possa ottenere con la micro-ottimizzazione.
  • Usi strutture adeguate? Supponiamo che tu stia utilizzando un Listquando ne hai bisogno in HashSetmodo da avere delle O(n)ricerche quando puoi O(1).
  • Usi il parallelismo in modo efficiente? Attualmente anche i telefoni cellulari possono avere 4 o più core e potrebbe essere allettante utilizzare i thread. Tuttavia non sono un proiettile d'argento in quanto hanno un costo di sincronizzazione (senza contare che se il problema è legato alla memoria non hanno comunque senso).

Anche se decidi che devi eseguire la micro-ottimizzazione (che in pratica significa che il tuo software viene utilizzato in HPC, incorporato o utilizzato solo da un numero molto elevato di persone, altrimenti il ​​costo aggiuntivo di manutenzione supera i costi di tempo del computer) che ti serve per identificare gli hotspot (kernel) che si desidera accelerare. Ma probabilmente dovresti:

  1. Conosci esattamente la piattaforma su cui stai lavorando
  2. Conosci esattamente il compilatore su cui stai lavorando e quale ottimizzazione è in grado di fare e come scrivere codice idiomatico per abilitare tale ottimizzazione
  3. Pensa ai modelli di accesso alla memoria e a quanto puoi inserire nella cache (dimensione esatta di cui sai dal punto 1).
  4. Quindi, se sei obbligato al calcolo, pensa a riorganizzare il calcolo per salvare il calcolo

Come osservazione finale. Di solito l'unico problema che hai con le chiamate di metodo sono i salti indiretti (metodi virtuali) che non erano previsti dal predittore di diramazione (purtroppo il salto indiretto è il caso difficile). Tuttavia:

  • Java ha un JIT che in molti casi può prevedere il tipo di classe e quindi un obiettivo di salto in anticipo e quindi negli hotspot non dovresti avere molti problemi.
  • I compilatori C ++ spesso eseguono un'analisi del programma e almeno in alcuni casi possono prevedere l'obiettivo al momento della compilazione.
  • In entrambi i casi in cui l'obiettivo è stato previsto, l'allineamento dovrebbe funzionare. Se il compilatore non fosse in grado di eseguire le possibilità interne, non potremmo farlo anche noi.

0

La mia risposta probabilmente non si espanderà troppo sulle risposte esistenti, ma penso che i miei due centesimi possano essere utili.

Prima di tutto; sì, per modularità, di solito si rinuncia ad un certo tempo di esecuzione. Scrivere tutto nel codice assembly ti darà la massima velocità. Detto ciò...

Conosci YouTube? Probabilmente il sito con la larghezza di banda più elevata esistente, o secondo a Netflix? Scrivono gran parte del loro codice in Python, che è un linguaggio altamente modulare non abbastanza sviluppato per prestazioni di alto livello.

Il fatto è che quando qualcosa va storto e gli utenti si lamentano del lento caricamento dei video, non ci sono molti scenari in cui quella lentezza alla fine sarebbe attribuita alla bassa velocità di esecuzione di Python. Tuttavia, la rapida ricompilazione di Python e la sua capacità modulare di provare nuove cose senza controllo del tipo probabilmente consentiranno agli ingegneri di eseguire il debug di ciò che non va abbastanza rapidamente ("Wow. Il nostro nuovo stagista ha scritto un ciclo che esegue una nuova sottoquery SQL per OGNI risultato. ") oppure (" Oh, Firefox ha deprecato il vecchio formato di intestazione della cache; e hanno creato una libreria Python per configurare facilmente il nuovo ")

In tal senso, anche in termini di tempo di esecuzione, un linguaggio modulare può essere considerato più veloce perché una volta individuati i colli di bottiglia, sarà probabilmente più semplice riorganizzare il codice per farlo funzionare nel modo migliore. Tanti ingegneri ti diranno che i grandi successi nelle prestazioni non erano dove pensavano che sarebbero stati (e in effetti le cose che hanno ottimizzato DID non erano quasi necessarie; o, non hanno nemmeno funzionato come si aspettavano!)


0

Sì e no. Come altri hanno notato prima il programma per la leggibilità, quindi per l'efficienza. Tuttavia, esistono pratiche standard che sono sia leggibili che efficienti. La maggior parte del codice viene eseguita piuttosto raramente e non otterrai comunque molti vantaggi dall'ottimizzazione.

Java può incorporare chiamate di funzioni più piccole, quindi ci sono poche ragioni per evitare di scrivere le funzioni. Gli ottimizzatori tendono a funzionare meglio con un codice più semplice da leggere. Ci sono studi che mostrano scorciatoie che teoricamente dovrebbero funzionare più velocemente, in realtà impiegano più tempo. È probabile che il compilatore JIT funzioni meglio, il codice è più piccolo e spesso i pezzi eseguiti possono essere identificati e ottimizzati. Non l'ho provato, ma mi aspetterei una funzione di grandi dimensioni che è chiamata raramente non essere compilata.

Questo probabilmente non si applica a Java, ma uno studio ha scoperto che funzioni più grandi in realtà erano più lente a causa della necessità di un modello di riferimento di memoria diverso. Questo era specifico per hardware e ottimizzatore. Per i moduli più piccoli sono state utilizzate le istruzioni che funzionavano all'interno di una pagina di memoria. Questi erano più veloci e più piccoli delle istruzioni richieste quando la funzione non si adattava a una pagina.

Ci sono casi in cui vale la pena ottimizzare il codice, ma in genere è necessario profilare il codice per determinare dove si trova. Trovo che spesso non sia il codice che mi aspettavo.

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.