Come affrontare il problema di (compilare) una base di codice di grandi dimensioni?


10

Sebbene sia in grado di programmare, non ho ancora alcuna esperienza con il lavoro su grandi progetti. Quello che ho fatto finora è stato codificare piccoli programmi che vengono compilati in pochi secondi (vari esercizi c / c ++ come algoritmi, principi di programmazione, idee, paradigmi o semplicemente provare api ...) o lavorare su alcuni progetti più piccoli che erano realizzato in uno o più linguaggi di script (python, php, js) in cui non è necessaria la compilazione.

Il fatto è che, quando si codifica in un linguaggio di scripting, ogni volta che voglio provare se qualcosa funziona, eseguo semplicemente lo script e vedo cosa succede. Se le cose non funzionano, posso semplicemente cambiare il codice e riprovare eseguendo nuovamente lo script e continuando a farlo fino a quando non ottengo il risultato che volevo. Il punto è che non devi aspettare qualsiasi cosa da compilare e per questo è abbastanza facile prendere una grande base di codice, modificarla, aggiungere qualcosa o semplicemente giocarci - puoi vedere le modifiche all'istante.

Come esempio prenderò Wordpress. È abbastanza facile provare a capire come creare un plugin per questo. Per prima cosa crei un semplice plug-in "Hello World", quindi crei una semplice interfaccia per il pannello di amministrazione per familiarizzare con l'API, quindi lo crei e rendi qualcosa di più complesso, nel frattempo cambiando l'aspetto di un paio di volte .. L'idea di dover ricompilare qualcosa di grosso come WP più e più volte, dopo ogni piccola modifica per provare "se funziona" e "come funziona / si sente" sembra semplicemente inefficiente, lenta e sbagliata.

Ora, come potrei farlo con un progetto scritto in una lingua compilata? Vorrei contribuire ad alcuni progetti open source e questa domanda continua a infastidirmi. La situazione probabilmente differisce da progetto a progetto in cui alcuni di essi che sono stati pre-pensati saggiamente saranno in qualche modo "modulari", mentre altri saranno solo un grosso blob che deve essere ricompilato ancora e ancora.

Vorrei sapere di più su come è fatto correttamente. Quali sono alcune pratiche, approcci e progetti (schemi?) Comuni da affrontare? Come viene chiamata questa "modularità" nel mondo dei programmatori e cosa devo cercare su Google per saperne di più? Spesso i progetti nascono dalle loro proporzioni del primo pensiero che diventano problematiche dopo un po '? Esiste un modo per evitare la lunga compilazione di progetti non così ben progettati? Un modo per modulare in qualche modo (forse escludendo parti non vitali del programma durante lo sviluppo (altre idee?))?

Grazie.


4
Ob. XKCD e la relativa maglietta thinkgeek * 8 ')
Mark Booth,

1
Se lavori su un progetto abbastanza grande con un budget abbastanza grande puoi ottenere server di compilazione per fare la compilazione per te :)
SoylentGray,

@Chad - Lo so, ma al momento sono solo io e la mia macchina desktop gnu / linux a casa :)
pootzko,

@Chad Ok, quindi ci stai dicendo che abbiamo bisogno di server dedicati per gestire il grosso di Java (o di qualsiasi altro linguaggio compilato)? È una schifezza totale
Kolob Canyon,

1
@KolobCanyon - No, sto dicendo che esiste una scala su cui potresti lavorare che li richiederebbe. e che sono abbastanza economici ora che avere una VM on demand dedicata alla compilazione e all'autmation veloci dei test è abbastanza facile da rendere la scala non così grande.
SoylentGray,

Risposte:


8

Proprio come è stato detto, non si ricompila mai l'intero progetto ogni volta che si effettua una piccola modifica. Invece si ricompila solo la parte del codice che è stata modificata, così come tutto il codice a seconda di esso.

In C / C ++, la compilazione è piuttosto semplice. È compilare tradurre ogni file sorgente in codice macchina (li chiamiamo oggetto file * .o) e poi si collegano tutti i file oggetto in un unico grande eseguibile.

Proprio come menzionato MainMa, alcune librerie sono integrate in file separati, che verranno collegati dinamicamente in fase di esecuzione con l'eseguibile. Queste librerie sono denominate Shared Objects (* .so) in Unix e Dynamically Linked Libraries (DLL) in Windows. Le librerie dinamiche hanno molti vantaggi, uno dei quali è che non è necessario compilarli / collegarli, a meno che il loro codice sorgente non cambi effettivamente.

Esistono strumenti di automazione della build che ti aiutano a:

  • Specifica le dipendenze tra le diverse parti dell'albero di origine.
  • Lancia compilazioni puntuali e discrete solo nella parte che è stata modificata.

I più famosi (make, ant, maven, ...) sono in grado di rilevare automaticamente quali parti del codice sono state modificate dall'ultima compilazione ed esattamente quale oggetto / binario deve essere aggiornato.

Tuttavia, questo comporta il costo (relativamente piccolo) di dover scrivere uno "script di build". È un file che contiene tutte le informazioni sulla tua build, come definire le destinazioni e le loro dipendenze, definire quale compilatore vuoi e quali opzioni usare, definire l'ambiente di compilazione, i percorsi della tua libreria, ... Forse hai sentito parlare di Makefile (molto comune nel mondo Unix) o build.xml (molto popolare nel mondo Java). Questo è ciò che fanno.


2
Ant (Java) non è in grado di determinare ciò che deve essere ricompilato. Gestisce la parte banale del lavoro, ricompilando il codice sorgente modificato, ma non comprende affatto le dipendenze di classe. Facciamo affidamento sugli IDE per questo, e vanno male se una firma del metodo viene cambiata in un modo che non richiede una modifica nel codice chiamante.
Kevin Cline,

@kevincline In secondo luogo - ANT compila tutto a meno che non specifichi qualcosa di diverso nel build.xmlfile
Kolob Canyon

7

Non ricompilare l'intero progetto ogni volta. Ad esempio, se si tratta di un'applicazione C / C ++, è probabile che venga separata in librerie (DLL in Windows), ogni libreria viene compilata separatamente.

Il progetto stesso è generalmente compilato quotidianamente su un server dedicato: quelli sono build notturne. Questo processo può richiedere molto tempo, poiché includeva non solo il tempo di compilazione, ma anche il tempo impiegato per eseguire unit test, altri test e altri processi.


3
Se io non ricompilare tutto poi quando ti ho tempo per giocare con il mio Trebuchet
SoylentGray

5

Penso che ciò che tutte le risposte finora abbiano alluso anche sia che i grandi progetti software sono quasi sempre suddivisi in pezzi molto più piccoli. Ogni pezzo è normalmente memorizzato nel suo file.

Questi pezzi sono compilati individualmente per creare oggetti. Gli oggetti vengono quindi collegati insieme per formare il prodotto finale. [In un certo senso, è un po 'come costruire cose da Legos. Non provi a plasmare l'ultima cosa da un grosso pezzo di plastica, invece combini un mucchio di pezzi più piccoli per farlo.]

Suddividere il progetto in pezzi che sono stati compilati individualmente consente che accadano alcune cose pulite.

Edificio incrementale

Prima di tutto, quando cambi un pezzo, di solito non devi ricompilare tutti i pezzi. In generale, purché non modifichi il modo in cui gli altri pezzi interagiscono con il tuo pezzo, gli altri non devono essere ricompilati.

Questo dà origine all'idea di costruzione incrementale . Quando si esegue una build incrementale, vengono ricompilati solo i pezzi interessati dalla modifica. Questo accelera notevolmente i tempi di sviluppo. È vero, potresti dover ancora aspettare che tutto venga ricollegato, ma che è comunque un risparmio rispetto a dover ricompilare e ricollegare tutto. (A proposito: alcuni sistemi / lingue supportano il collegamento incrementale in modo da ricollegare solo le cose che sono cambiate. Il costo per questo di solito è in prestazioni e dimensioni del codice scarse.)

Test unitari

La seconda cosa che ti permette di fare piccoli pezzi è guardare individualmente i pezzi prima che vengano combinati. Questo è noto come Unit Testing . In Unit Testing, ogni unità viene testata individualmente prima di essere integrata (combinata) con il resto del sistema. I test unitari vengono normalmente scritti in modo da poter essere eseguiti rapidamente senza coinvolgere il resto del sistema.

Il caso limite dell'applicazione del test è visto in Test Driven Development (TDD). In questo modello di sviluppo, nessun codice viene scritto / modificato se non per correggere un test fallito.

Rendendolo più facile

Quindi rompere le cose sembra buono, ma sembra anche che ci sia bisogno di molto lavoro per costruire il progetto: devi capire quali sono i pezzi cambiati e cosa dipende da quei pezzi, compilare ogni pezzo e quindi collegare tutto insieme.

Fortunatamente, i programmatori sono pigri *, quindi inventano molti strumenti per facilitare il loro lavoro. A tal fine, sono stati scritti molti strumenti per automatizzare l'attività sopra. I più famosi sono già stati menzionati (marca, formica, maven). Questi strumenti ti consentono di definire quali pezzi devono essere messi insieme per realizzare il tuo progetto finale e in che modo i pezzi dipendono l'uno dall'altro (cioè se lo cambi, questo deve essere ricompilato). Il risultato è che l'emissione di un solo comando fa capire cosa deve essere ricompilato, lo compila e ricollega tutto.

Ma questo lascia ancora capire come le cose si relazionano l'una con l'altra. È un sacco di lavoro e, come ho detto prima, i programmatori sono pigri. Quindi hanno escogitato un'altra classe di strumenti. Questi strumenti sono stati scritti per determinare le dipendenze per te! Spesso gli strumenti fanno parte degli ambienti di sviluppo integrato (IDE) come Eclipse e Visual Studio, ma ce ne sono anche alcuni autonomi utilizzati sia per applicazioni generiche che specifiche (programmi makedep, QMake per Qt).

* In realtà, i programmatori non sono davvero pigri, amano solo passare il tempo a lavorare sui problemi, non a svolgere attività ripetitive che possono essere automatizzate da un programma.


5

Ecco il mio elenco di cose che puoi provare per velocizzare le build C / C ++:

  • Sei pronto a ricostruire solo ciò che è cambiato? La maggior parte degli ambienti lo fa per impostazione predefinita. Non è necessario ricompilare un file se questo o nessuna delle intestazioni è cambiata. Allo stesso modo, non c'è motivo di ricostruire una dll / exe se tutti i link collegati in objs / lib non sono cambiati.
  • Inserisci elementi di terze parti che non cambiano mai e le intestazioni associate in alcune aree della libreria di codici di sola lettura. Hai solo bisogno delle intestazioni e dei binari associati. Non dovresti mai aver bisogno di ricostruire questo da fonti diverse da forse una volta.
  • Quando ho ricostruito tutto, i due fattori limitanti nella mia esperienza sono stati il numero di core e la velocità del disco . Ottieni un potente quad core, hyperthreaded machine con un ottimo hdd e le tue prestazioni miglioreranno. Prendi in considerazione un'unità a stato solido: tieni presente che quelli economici potrebbero essere peggio di un buon hdd. Valuta di usare raid per aumentare il tuo hdd
  • Utilizzare un sistema di compilazione distribuito come Incredibuild che suddividerà la compilazione su altre stazioni di lavoro sulla rete. (Assicurati di avere una rete solida).
  • Imposta una build di unità per salvarti dal ricaricare costantemente i file di intestazione.

Nella mia esperienza (non molto, ma bene) la velocità del disco inizia a diventare irrilevante se il tuo progetto va oltre "molto piccolo". Pensa a quello che dici nel prossimo punto elenco: stai usando la rete per accelerare la compilazione. Se il disco era un grosso collo di bottiglia, ricorrere alla rete non sembra una mossa molto buona.
R. Martinho Fernandes,

Un'altra soluzione economica è quella di compilare in un tmpfs. Può aumentare notevolmente le prestazioni se il processo di compilazione è associato a IO.
Artefatto2,

4

L'idea di dover ricompilare qualcosa di grosso come WP più e più volte, dopo ogni piccola modifica per provare "se funziona" e "come funziona / si sente" sembra solo inefficiente, lenta e sbagliata.

Eseguire qualcosa di interpretato è anche molto inefficiente e lento e (probabilmente) sbagliato. Ti stai lamentando dei requisiti di tempo sul PC dello sviluppatore, ma la non compilazione causa requisiti di tempo sul PC dell'utente , il che è probabilmente molto peggio.

Ancora più importante, i sistemi moderni possono fare ricostruzioni incrementali piuttosto avanzate e non è comune ricompilare il tutto per modifiche minori, i sistemi compilati possono includere componenti di script, specialmente comuni per cose come l'interfaccia utente.


1
Credo che la mia domanda non dovesse essere interpretata rispetto al dibattito sull'approccio alla compilazione. Invece ho appena chiesto un consiglio su come lo sviluppo di un grande progetto (compilato) è fatto correttamente. Grazie comunque per l'idea di ricostruzioni incrementali.
pootzko,

@pootzko: Beh, è ​​abbastanza ingiusto discutere gli aspetti negativi della compilazione quando non stai parlando anche degli aspetti negativi dell'interpretazione.
DeadMG,

1
no non lo è. è un altro dibattito e non ha nulla a che fare con la mia domanda. Non sto dicendo che è qualcosa che non dovrebbe essere discusso. dovrebbe, ma non qui.
pootzko,

@pootzko: Quindi non dovresti dedicare la maggior parte delle tue domande all'enumerazione di ciò che non ti piace della compilazione. Avresti dovuto scrivere qualcosa di molto più breve e più succint, come "Come si possono ridurre i tempi di compilazione di grandi progetti?".
DeadMG,

Non sapevo di dover chiedere a qualcuno come "avrei dovuto" porre la mia domanda ..? : O l'ho scritto come ho fatto per spiegare meglio il mio punto di vista in modo che altri possano capirlo meglio e spiegarmi come ottenere la stessa cosa / simile con i linguaggi compilati. Ancora una volta, non ho chiesto a nessuno di dirmi se le lingue interpretate causano requisiti di tempo peggiori sul PC dell'utente. Lo so, e non ha nulla a che fare con la mia domanda: "come si fa con i linguaggi compilati", scusa. Altre persone sembrano aver capito cosa ho chiesto, quindi non credo che la mia domanda non sia abbastanza chiara ..
pootzko,

4
  • Ricostruzione parziale

Se il progetto implementa il DAG di dipendenza della compilazione corretto, è possibile cavarsela solo ricompilando i file oggetto interessati dalla modifica.

  • Processo di compilazione multipla

Supponendo anche un DAG di dipendenza compilazione corretto, è possibile compilare utilizzando più processi. Un lavoro per core / cpu è la norma.

  • Test eseguibili

È possibile creare più eseguibili per i test che collegano solo file di oggetti particolari.


2

Oltre alla risposta di MainMa, abbiamo appena aggiornato le macchine su cui lavoriamo. Uno dei migliori acquisti che abbiamo fatto è stato un SSD per quando non puoi fare a meno di ricompilare l'intero progetto.

Un altro suggerimento sarebbe quello di provare un compilatore diverso. Nel passato, passiamo dal compilatore Java a Jikes e ora siamo passati all'utilizzo del compilatore in bundle con Eclipse (non so se ha un nome) che sfrutta meglio i processori multicore.

Il nostro progetto di 37.000 file ha richiesto circa 15 minuti per essere compilato da zero prima di apportare queste modifiche. Dopo le modifiche è stato ridotto a 2-3 minuti.

Naturalmente, vale la pena ricordare di nuovo il punto di MainMa. Non ricompilare l'intero progetto ogni volta che vuoi vedere un cambiamento.

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.