Limitare lo stack delle chiamate può aiutarti a usare meno astrazioni?

2

Ho seguito una presentazione di Jonathan Blow sulla qualità del software. Come punto che aggiungere sempre più livelli di astrazione ti dà non solo più difficoltà a gestire il tuo codice, ma anche stiamo utilizzando troppe risorse e il software è lento.

Su questo mostra anche un gigantesco stack di chiamate di qualche server tomcat. Non so cosa funzioni Tomcat, ma a mio avviso nessun codice server deve essere così astratto.

Anche in The Art of Unix Programming l'autore indica che è meglio avere rappresentazioni piatte e anche sposta le parti algoritmiche nelle parti dati.

Con questo in mente, stavo pensando che uno strumento o un approccio di controllo delle prestazioni avrebbe tracciato la dimensione dello stack di chiamate. Questa sarebbe una grande unità di misura dell'astrazione e della performance. Ovviamente devi interrompere il limite di stack delle chiamate, in alcuni algoritmi ricorsivi, ma la dimensione complessiva dello stack delle chiamate deve essere mantenuta sotto una certa soglia.

Che ne pensi? Limitare / rintracciare lo stack delle chiamate potrebbe portare qualche vantaggio o no? Hai qualche consiglio / esperienza? O è solo un'idea stupida?

    
posta microo8 17.08.2017 - 14:04
fonte

2 risposte

11

Sono d'accordo sul fatto che le astrazioni influenzino lo stack di chiamate nel modo in cui hai descritto. Sono anche d'accordo sul fatto che limitare lo stack di chiamate può darti meno astrazioni. Ma ci sono due ragioni principali per me per non limitare lo stack di chiamate:

Meno astrazioni non sono necessariamente buone

Riducendo le astrazioni rischi anche di perdere molte astrazioni utili che abbiamo costruito nel corso degli anni. Questo è il vero problema delle astrazioni: alcune sono estremamente preziose per noi, ma allo stesso tempo, altre possono intromettersi e complicare le cose inutilmente.

Sfortunatamente, non è chiaro quale astrazione appartenga al lato di questa moneta. Si differenzia anche in base al tuo progetto e al tuo team. Quello che una squadra trova essere un'astrazione complicata è solo una normale programmazione quotidiana per un altro team. Qual è un'astrazione davvero utile per un progetto può essere eccessivo per un altro. Non è abbastanza chiaro da giustificare qualsiasi tipo di misura volta a ridurre le astrazioni.

Il callstack più piccolo non è necessariamente buono

Un problema simile appare quando si guardano le pile di chiamate. È vero, uno stack di chiamate che si estende su più pagine sullo schermo non è una cosa piacevole. Ma ogni sviluppatore deve imparare comunque a leggere lo stack di una chiamata, dal momento che già i programmi di base possono facilmente arrivare a una dozzina di chiamate o più. La domanda interessante però è quanto sia difficile interpretare lo stack delle chiamate. Se è pieno di astrazioni che rendono complicato trovare ciò che stai cercando, allora questo è certamente sbagliato.

D'altra parte, non tutte le lunghe pile di chiamate sono cattive. Soprattutto quando si tratta di leggibilità del codice, molti sviluppatori consigliano di limitare le dimensioni dei metodi. Poiché è ancora necessario implementare la stessa logica complicata, è necessario ricorrere alla suddivisione del codice in più metodi. Ciò a sua volta aumenta la dimensione dello stack di chiamate, ma non aggiunge molta complessità, poiché l'unica astrazione coinvolta è una semplice chiamata di metodo.

D'altra parte, se si limita la dimensione dello stack di chiamata, uno sviluppatore non può più suddividere il suo codice in metodi facilmente comprensibili. Invece è necessario ricorrere al raggruppamento di tutto il codice in un unico metodo, che aumenta drammaticamente la complessità, complica la manutenzione e riduce la leggibilità.

È una buona idea?

Io non la penso così Come discusso sopra, non sembra esserci una relazione diretta tra complessità e dimensioni dello stack di chiamata. In effetti, ci sono casi standard in cui è vero il contrario. Dato che non è garantito che l'effetto desiderato sia presente, non lo farei.

    
risposta data 17.08.2017 - 15:20
fonte
2

Abstraction vs performance di runtime

Oggigiorno Java fornisce molte abstracions per svilupparsi molto più velocemente della vecchia C al costo di un po 'di prestazioni sì, ma di solito non hanno importanza per quello che stai facendo.

Framework come Spring, server come Tomcat sono prodotti sviluppati da anni con sviluppatori di sviluppatori che sono molto più competenti degli sviluppatori medi. Fagioli primavera & la gestione delle transazioni produce stack abbastanza brutti dal momento che deve creare proxy della tua classe, ma non è lento.

Se usi il filtro web dalla JSR e ne accatasti più catene, le tue pile appariranno come questa (da cima a fondo!):

at [.apache..].doFilter(ApplicationFilterChain.java:207)
 [.apache..].internalDoFilter(ApplicationFilterChain.java:240)
 [...your filter..].doFilter(DelegatingFilterProxy.java:262) 
 [.apache..].doFilter(ApplicationFilterChain.java:207)
 [.apache..].internalDoFilter(ApplicationFilterChain.java:240)
 [...your 2nd filter..].doFilter(DelegatingFilterProxy.java:262) 
 ...

Quindi se filtri a catena per:

  • Materiale di registrazione
  • Autenticazione
  • Autorizzazione
  • Avvia trasferimento database
  • Alcuni filtri per framwork

Puoi avere già 20 linee di quel tipo nei tuoi stack, ma la maggior parte di questi filtri di solito esegue pochissime operazioni. Anche se ottieni fino a 1000 operazioni (Java più in assembly ma non ha importanza), non è nulla al giorno d'oggi.

Inoltre, se hai un numero minore di stack di chiamate, ciò non significa che stai diventando più veloce, puoi semplicemente avere una gerarchia più piatta della funzione, che esegue lo stesso numero di istruzioni.

Preoccuparsi della lunghezza dei tuoi stack di chiamate è inutile anche in C, a meno che tu non stia facendo qualcosa di estremamente specifico (kernel?), stai sprecando le tue energie preoccupandoti di ciò. Ridurre il numero di parametri di una funzione utilizzando una struct / class è la maggior parte dell'ottimizzazione più utile ma non necessaria per la maggior parte del tempo (forse se si passa attraverso quella funzione 1 000 000 000 di volte?)

Hai difficoltà a leggere uno stack java? Il 99% delle volte che devi leggerlo, il tuo stack sarà simile a questo:

Caused by : bla bla bla
 at bla bla bla
 at bla bla
 at bla bla bla 
...
Caused by : bla bla bla 2
 at bla bla bla
 at bla bla
 at bla bla bla 
 ...
Caused by : bla bla bla 3
 at bla bla bla
 at bla bla
 at bla bla bla 
 ...

Dove [...] è di circa 10-40 linee di pile. Di solito sono inutili, trovare in ognuno di essi causato dal bisogno di scavare attraverso la prima chiamata della classe e troverai facilmente ciò che è sbagliato nella maggior parte dei casi. Leggo poco più di 10 righe di stack in 200 centinaia +, quando l'errore non è nel primo "causato da", di loro per trovare ciò che è sbagliato.

Ho fatto solo gli sviluppi del web fino ad ora, e devo ancora vedere il mio codice rallentare a causa di tomcat / spring al posto mio.

A parte questo:

Pensi ancora che Tomcat sia lento e provi con il benchmarking che devi guadagnare un po 'di tempo dal suo livello? C'è l'APR del connettore nativo, che cambia l'implementazione del connettore Java in uno nativo.

Puoi anche regolare altri parametri, e JVM, questo richiede conoscenza sì, ma non è lo stesso quando usi le opzioni specifiche del compilatore gcc / g ++ / VS?

Astrazione rispetto al tempo di sviluppo

Si potrebbe dire facilmente quanto segue: più astrazioni = più classi = tempi di sviluppo più lenti.

Questo è facile da dire e cercherò di affrontarlo attraverso alcuni punti.

L'astrazione * ha l'obiettivo di nascondere la cosa inutile e costringerti a usarla correttamente senza preoccuparti dei dettagli delle implementazioni, quindi significa che durante la codifica, il tuo codice tenderà a essere più stabile. Ci vorrà più tempo per pensare correttamente come scrivere il tuo codice con questi livelli di astrazione, ma perché sono molto meno i dettagli di cui preoccuparsi: scriverai codice più breve e più stabile. Quindi sì, scriverai meno righe di codici all'ora, ma alla fine finirai con meno linee di codice, meno volte a fare il debug.

Se c'è una trappola con le astrazioni non si tratta di tempo di sviluppo grezzo, si tratta della curva di apprendimento per essere in grado di sapere come usarli correttamente: ogni nuovo framework / libreria aumenta la curva di apprendimento e rende più difficile la svolta per gestire quale può finire con più tempo di sviluppo perché si finisce con novizi che non rimangono abbastanza a lungo.

Perché quando non sai come dovrebbero funzionare le astrazioni, passerai molto tempo a scrivere quelle 5 linee di codici e eseguirne il debug all'inizio.

Per me il modo migliore per gestire questa curva di apprendimento è:

  • Evita l'uso esotico / "mannaia" dei tuoi strumenti per tenerlo all'utilizzo standard.
  • Mantieni il più semplice possibile. Non aggiungere strutture non necessarie e isolare i componenti che utilizzano ciascuno di essi in livelli diversi.
  • Metti un modo standard per fare alcune cose comuni attraverso l'app. Deve essere breve in modo che le persone si riferiscano ad esso più facilmente. Inoltre ricorderanno quando faranno qualcosa che è documentato.
  • Essere (o colui che sa) a disposizione per rispondere alle domande, non solo rispondere a "come farlo funzionare", ma perché deve essere fatto in questo modo.

  • : ovviamente, considerando che il lavoro è stato svolto abbastanza bene, non povere / astrazioni ingegneristiche.

risposta data 17.08.2017 - 16:55
fonte

Leggi altre domande sui tag