mercoledì 4 settembre 2013

Ottimizzare la compilazione su Fedora con distcc e ccache

In questa guida vedremo come installare e configurare distcc e ccache, due strumenti che permettono di abbreviare (e anche di parecchio) i tempi di compilazione.
Distcc è un programma ideato per distribuire i processi di compilazione tra più host all'interno di una rete; è quindi ovviamente necessario disporre di almeno due o più macchine della stessa architettura per potere usufruire dei benefici di questo strumento. In mancanza di questo requisito distcc diventa perfettamente inutile e non ha alcune senso installarlo.
Ccache invece (come riporta wikipedia nell'abbozzo dedicato al programma) "memorizza l'output della compilazione di un programma scritto in C/C++ così che la volta successiva la medesima compilazione possa essere evitata. Questo metodo velocizza di gran lunga il tempo di compilazione."

Vediamo ora come installarli e configurarli in modo da farli lavorare congiuntamente.
Partiamo da ccache:
# yum install ccache
$ source /etc/profile.d/ccache.sh
Se ora provate ad esempio a dare:
ll $(which g++)
lrwxrwxrwx. 1 root root 16  7 lug 23.04 /usr/lib64/ccache/g++ -> ../../bin/ccache
potete notare come il path di g++ (default /usr/bin/g++) sia stato cambiato e punti ora all'eseguibile di ccache. Questo ci garantisce che ogni processo di compilazione verrà gestito dal programma il quale stabilirà di volta in volta se andare a "pescare" i dati dalla sua cache o passare i sorgenti ai compilatori della GNU Compiler Collection.
Non occorrono ulteriori configurazione per usufruire dei vantaggi di ccache, semplicemente lanciate le vostre compilazioni nello stesso identico modo che seguivate prima dell'installazione del programma.

Passiamo all'installazione di distcc (qui le cose si complicano un pochino). Su tutte le macchine della nostra rete che vogliamo utilizzare per le compilazioni "condivise" iniziamo con il dare:
# yum install distcc-server
quindi editiamo la configurazione aprendo il file /etc/sysconfig/distccd.
Ipotizzando di disporre di due macchine con indirizzi 192.168.1.1 e 192.168.1.2 modificheremo il file in questo modo:
OPTIONS="--allow 192.168.1.0/24 --log-file=/var/tmp/distccd.log"
USER="nobody"
--allow 192.168.1.0/24 abilita tutti gli host appartenenti alla rete 192.168.1.x a connettersi al demone distccd.
--log-file abilita la creazione di un file di log. Senza questa opzione distccd invierà i suoi output esclusivamente al file /var/log/messages. Se desiderate che il log memorizzi esclusivamente eventuali messaggi di errore aggiungete l'opzione --log-level error.
USER="nobody" indica invece che il demone girerà con i privilegi dell'utente "nobody", il che dovrebbe garantirci un buon livello di sicurezza.
Se non specificato diversamente distccd girerà sulla porta 3632 (default). Potete indicare una porta differente con l'opzione --port. Dovrete però ricordavi anche di aggiornare opportunamente la configurazione del client (come vedremo più avanti)

Completiamo la configurazione di distcc lato server aprendo la porta 3632 nel firewall (ricordiamoci anche in questo caso di eseguire l'operazione su tutti gli host). Senza bisogno di ricorrere a firewall-config possiamo comodamente aggiornare le impostazioni di firewalld con un'unica istruzione:
# firewall-cmd --permanent --zone=$(firewall-cmd --get-default-zone) --add-port=3632/tcp
controlliamo che la porta 3632 sia stata effettivamente aggiunta all'elenco delle porte aperte:
 # firewall-cmd --permanent --zone=$(firewall-cmd --get-default-zone) --list-ports
Avviamo il demone distccd con:
# systemctl enable distccd
# systemctl start distccd
Il demone creerà una serie di sottoprocessi (di default pari al doppio delle cpu/core del sistema più uno) corrispondenti al numero massimo di compilazioni che possono essere eseguite parallelamente (quindi su un singole core avremo tre processi, su un dual core saranno invece cinque e così via). Possiamo verificarlo con:
$ ps -ef | grep distccd
nobody    3417     1  0 21:09 ?        00:00:00 /usr/bin/distccd --verbose --no-detach --daemon --allow 192.168.1.0/24 --log-file=/var/tmp/distccd.log
nobody    3418  3417  0 21:09 ?        00:00:00 /usr/bin/distccd --verbose --no-detach --daemon --allow 192.168.1.0/24 --log-file=/var/tmp/distccd.log
nobody    3419  3417  0 21:09 ?        00:00:00 /usr/bin/distccd --verbose --no-detach --daemon --allow 192.168.1.0/24 --log-file=/var/tmp/distccd.log
nobody    3420  3417  0 21:09 ?        00:00:00 /usr/bin/distccd --verbose --no-detach --daemon --allow 192.168.1.0/24 --log-file=/var/tmp/distccd.log
nobody    3421  3417  0 21:09 ?        00:00:00 /usr/bin/distccd --verbose --no-detach --daemon --allow 192.168.1.0/24 --log-file=/var/tmp/distccd.log
L'output riporta cinque demoni distccd, valore corretto per un macchina dual core. Possiamo anche forzare la creazione di un numero arbitrario di processi con l'opzione --jobs sempre nel file /etc/sysconfig/distccd.

Installiamo ora il client di distcc su una o più macchine che useremo per lanciare le attività di compilazione (ovvero il (o i) computer su cui eseguiremo i vari "make", "rpmbuild",ecc.). Da un terminale diamo:
# yum install distcc
Fatto questo dobbiamo far sapere a distcc quali sono gli host tra i quali distribuire i lavori di compilazione; per far questo è sufficiente indicare nel file /etc/distcc/hosts i relativi indirizzi ip. Continuando con il nostro esempio il nostro file hosts sarebbe così composto:
192.168.1.1 192.168.1.2
Non occorre specificare la porta se i nostri server girano su quella di default; in caso contrario è invece necessario specificarla con la sintassi [host]:[port], ad esempio:
192.168.1.1:1234 192.168.1.2:1234
All'interno sempre dello stesso file è inoltre possibile indicare alcune opzioni che permettono di modificare il comportamento di distcc. La più interessante è a mio avviso lzo (per le altre opzioni vi rimando direttamente alla lettura di man distcc) che, come intuibile, fa sì che tutti i dati vengano compressi prima di essere trasferiti da un host all'altro. Nel mio caso ho verificato che tale opzione permette un'ulteriore riduzione dei tempi di compilazione tutt'altro che trascurabile. Per attivare la compressione lzo, modificheremo il file hosts in questo modo:
192.168.1.1,lzo 192.168.1.2,lzo
Abbiamo praticamente terminato la nostra configurazione, l'unica cosa che ancora manca è quella di "legare" insieme distcc e ccache per creare un ambiente di compilazione ottimale; per far ciò ci basta la seguente istruzione:
 $ export CCACHE_PREFIX="distcc"
Vi consiglio di aggiungerla al vostro .bashrc in modo che venga eseguita automaticamente ad ogni login.
Non ci resta ora che effettuare qualche test per vedere come il nostro setup si comporta sul campo. Per misurare i tempi di compilazione possiamo usare il comando bash time:
$ time make
$ time rpmbuild -ba file.spec
Se usiamo make (come spesso avviene) per compilare un programma, è bene, per sfruttare al massimo le capacità di distcc, passare come numero di jobs la somma di tutti i demoni distccd della nostra rete. Cerco di spiegarmi meglio: abbiamo due host A e B; A è un singol core quindi può gestire al massimo n.3 processi di compilazione simultaneamente. B è invece un dual core quindi può arrivare a n.5 processi. Il valore quindi da passare a make è di 8 (3+5):
$ make -j 8
Utilizzare un numero superiore (in questo caso) ad 8 non avrebbe alcun senso, distcc automaticamente bloccherebbe le richieste eccedenti le capacità dei server.

Compilare più volte lo stesso programma provando opzioni differenti vi aiuterà a trovare la configurazione migliore per le vostre macchine. Prima di ogni test è però necessario pulire la cache di ccache, altrimenti i risultati dei vostri test risulterebbero completamente falsati.
Ccache permette infatti di ricompilare in maniere estremamente rapida un pacchetto compilato almeno già una volta, grazie alla sua capacità di memorizzazione dei risultati delle compilazioni precedenti. Questa feature utilissima va però "disattivata" in questo caso, pena avere dei risultati del tutto inaffidabili.
Ci basterà prima di ogni compilazione azzerare la cache con:
 $ ccache -C
Per monitorare in tempo reale l'avanzamento della compilazione e i trasferimenti dati tra le macchine possiamo lanciare distccmon-gnome o distccmon-text dall'host client.

Edit: alcuni tips&tricks
Nel file .rpmmacros nella vostra home è possibile impostare il numero di jobs predefinito che rpmbuild dovrà usare nella creazione dei pacchetti rpm: è sufficiente inserire il valore desiderato in corrispondenza del campo %_smp_mflags, ad esempio:
%_topdir %(echo $HOME)/rpmbuild
%_smp_mflags    -j12
...
Se desiderate eseguire una particolare compilazione senza l'uso di distcc vi basta "annullare" la variabile CCACHE_PREFIX impostata in precedenza:
$ env -u CCACHE_PREFIX rpmbuild -ba file.spec

Nessun commento:

Posta un commento