Preliminares
-
Incluso el proyecto de software más sencillo necesita de varios ficheros que dependen entre ellos de distinta manera.
-
Estos ficheros deben compilarse, enlazarse, etc… y si se modifica alguno, se ha de repetir el proceso. En proyectos pequeños esto no importa mucho…pero en proyectos grandes sí que lo hace. En este thread de StackOverflow tienes algunos otros ejemplos de tiempos de compilación de otras versiones de Windows. Dado que el código del S.O. Windows es cerrado puedes comparar con la alternativa libre llamada ReactOS.
-
Y en este vídeo de Dave Plummer en su canal de youtube puedes comprobar cómo se comporta un hardware actual (2023) con un proyecto como es Google Chrome.
-
La herramienta
make
simplifica todo este proceso. Para ello tiene en cuenta las fechas de la última modificación de cada fichero. -
Podemos concluir por tanto que
make
es un generador de órdenes en base a marcas de tiempo.
Make (I)
-
Nos da información de lo que hace y porqué con la opción
-d
. -
Con la opción
-k
continúa la ejecución aunque haya errores. -
Al invocar a
make
podemos dar valor a variables, por ejemplo: "make CC=clang
". Cambia el compilador deC
guardado en la variableCC
. El compilador deC++
suele guardarse en la variableCXX
.
Make (II). Fichero Makefile
-
Es un fichero en formato ASCII.
-
Se permiten comentarios, comienzan por un símbolo
#
. -
El
Makefile
más sencillo consta sólo de reglas:
objetivo ... : dependencias
(TAB) orden
(TAB) ...
(TAB) ...
Make (III). Fichero Makefile
objetivo o target
-
Nombre de un fichero a generar o el de una acción a ejecutar (
clean
) — objetivo especial.PHONY
— . dependencias
-
Es una lista de ficheros de los que depende el
objetivo
. orden
-
Son las acciones que
make
realiza para obtener elobjetivo
.
El objetivo
se puede separar de las dependencias
por un
"::
". Si hay varias reglas con el mismo objetivo
, se comportan
como si fueran diferentes.
Para un mismo objetivo
sólo podemos emplear un tipo de
separador, o ":
" o "::
".
Make (IV). Ejemplo Makefile
edit : main.o kbd.o command.o display.o insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o insert.o search.o \
files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
..........
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
Make (V). Ejemplo Makefile con variables
objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
..........
.PHONY : clean
clean :
rm edit $(objects)
- $?
-
Representa la lista de dependencias de una regla que son más jóvenes que el objetivo actual.
- $^
-
Representa la lista completa de dependencias.
- $@
-
Representa el objetivo actual.
Make (VI).
-
Cada proyecto tendrá un Makefile general asociado, y éste siempre tendrá (al menos) los siguientes objetivos:
-
all: Será el primero y representa el objetivo por defecto.
-
clean: Borra todos los ficheros intermedios que se puedan volver a generar al ejecutar
make
. -
dist: Crea un fichero
.tar.gz
que contiene la distribución del proyecto. -
install: Instala el resultado de crear el proyecto.
-
uninstall: Desinstala el resultado de crear el proyecto.
-
-
Make puede lanzar compilaciones en paralelo.
-
Para ello debes emplear la opción
-j
seguida del numero de trabajos en paralelo a lanzar, por ejemplo:make -j2
-
Lanzar trabajos en paralelo suele descubrir fallos a la hora de especificar las dependencias, no es lo mismo satisfacerlas de forma secuencial que en paralelo.
Make (VII). Multinivel
-
Si el proyecto que desarrollamos tiene la entidad suficiente, lo dividiremos en varios módulos, estando el código de cada uno de ellos en un subdirectorio.
-
Cada subdirectorio tendrá su propio `Makefile` y el directorio principal del proyecto tendrá uno que gestiona los sub-`Makefile` de cada subdirectorio.
-
make
permite llamadas recursivas, de manera que elmake
ejecutado en el directorio principal irá invocando unmake
por cada subdirectorio. -
Hay desarrolladores que descartan el uso de
make
recursivo, introduce complejidades y puede ser más lento que usar un soloMakefile
en el directorio principal del proyecto.
Ccache (I)
-
Se trata de una herramienta basada en una idea muy sencilla: una caché para el compilador. De ahí su nombre: ccache.
-
¿Y cómo funciona? : Se cachean ( guardan — ~/.ccache/ — ) los resultados de compilaciones previas (ficheros "
.o
") los cuales se proporcionan instantaneamente cuando se detecta que se debe volver a compilar el mismo código otra vez. Por lo tanto no se deben volver a regenerar ( tiempo ahorrado ). -
La versión actual de
ccache
soporta los lenguajes: "C
", "C++
".", "`Objective-C
" y "`Objective-C++ -
ccache
está hecho de manera que produce la misma salida del compilador que obtendríamos si no lo estuvieramos usando. -
Esto es así hasta tal punto, que la única manera de saber que lo estamos usando es por los tiempos de compilación obtenidos.
Ccache (II). Características
-
Mantiene estadísticas de aciertos/fallos
-
Gestión automática del tamaño de la cache
-
Puede cachear compilaciones con "
warnings
". -
Es fácil de instalar
-
Añade una sobrecarga mínima al proceso de compilación
-
Opcionalmente puede usar enlaces duros cuando sea posible para evitar copias
-
Opcionalmente comprime los archivos en la cache para ahorrar sitio
Ccache (III). Limitaciones
-
Sólo cachea los resultados de compilaciones de ficheros individuales de C/C+\+/Objective-C/Objective-C++.
-
Solo funciona con GCC o compiladores que se comporten de forma similar.
-
No se soportan algunas opciones de compilación. Si se detecta alguna de esas opciones,
ccache
invocará automáticamente al compilador real.
Ccache (IV). Formas de ejecutarlo
ccache [opciones de ccache] gcc main.c -o main
cp ccache /usr/local/bin/
ln -s ccache /usr/local/bin/gcc
ln -s ccache /usr/local/bin/g++
ln -s ccache /usr/local/bin/cc
ln -s ccache /usr/local/bin/c++
Ccache (V). ¿Cómo funciona?
-
apt-get source tcc o desde su página web.
time make
make clean
time make
...
make clean
time make CC="ccache gcc"
make clean
time make CC="ccache gcc"
Por cierto, prueba a compilar tcc
con el propio tcc
:
¿Qué tiempos de compilación observas?
¿Qué pasa si tratas de usar tcc
con ccache
?
-
apt-get source wmaker o desde su página web.
Repetimos los mismos pasos que antes.
Ccache (VI). ¿Cómo funciona?
-
Se puede saber la configuración actual con:
ccache -p
-
Se puede guardar la configuración en un fichero
-
Interrogamos la
cache
:ccache -s
-
La borramos completamente (ojo!)…:
ccache -C
-
A nivel interno la manera que tiene
ccache
de saber cuándo recompilar un fuente y cuando no, es calculando la sumahash
de varias informaciones que deberían ser únicas en cada compilación. -
Emplea el algoritmo
MD4
…hoy en día en entornos criptográficos es débil, pero para lo que lo usaccache
(obtener un índice) es suficiente. -
ccache
puede trabajar en uno de tres modos:-
modo directo : es el usado por defecto
-
modo preprocesador : se emplea si se define la variable de entorno
CCACHE_NODIRECT
. -
modo de dependencias : en este modo no usa el preprocesador para nada.
-
-
El modo directo es el más rápido ya que no ejecuta el preprocesador
Sccache
Distcc (I)
-
ccache
es muy útil compilando todo en una máquina. -
Cuando el volumen del código que queremos compilar es muy grande…sería interesante poder distribuir la compilación del código.
-
Para que esta distribución de la compilación se haga de manera eficiente debemos ayudarnos de una herramienta como distcc
-
distcc
distribuye la compilación de códigoC
,C++
entre diversas máquinas conectadas en red., `Objective C
y `Objective C++ -
Se compone de un cliente (
distcc
) y de un servidor (distccd
). -
Es muy fácil de instalar y de usar.
-
Está pensado para ser usado con
gcc
pero en determinadas situaciones también puede ser usado con el compilador de C++ de Intel y el de Oracle. -
Se basa en una premisa:
distcc
siempre debe generar el mismo resultado que una compilación local.
Distcc (II). Funcionamiento
-
También tiene dos modos de funcionamiento: sencillo y bombeo (pump).
-
En modo sencillo
distcc
envía al servidor el código preprocesado y los argumentos para el compilador.-
El preprocesador se ejecuta localmente.
-
-
En modo bombeo envía al servidor el fichero a compilar y todos los archivos
#include
(de manera recursiva) que necesita para ser compilado. De estos#include
se excluyen aquellos que se encuentran en los directorios de cabeceras estandar del sistema.-
El preprocesador se ejecuta de forma remota.
-
Se estima que en este modo de funcionamiento puede distribuir los ficheros a compilar hasta 10 veces más rápido que en el modo sencillo.
-
-
La compilación es controlada por la máquina que ejecuta el cliente (el
pc
del programador) y es el clientedistcc
ejecutado en esta máquina el encargado de enviar el código a compilar a aquellas máquinas de la red que ejecutan el servidordistccd
.
Distcc (III). Funcionamiento
-
distcc
puede funcionar sobre TCP y sobre SSH, en este último caso es un poco más lento. -
El servidor
distccd
se puede ejecutar manualmente por un usuario, desde inetd o desde systemd. -
distcc
está pensado para hacer uso de la opción -j N demake
. Como regla sencilla el valor de N se puede ajustar al doble del número de CPUs disponibles en la red.
Distcc (IV). Guía rápida
-
En cada servidor de compilación ejecutamos:
distccd --daemon --allow IP-permitida --allow IP2 ...
-
Ponemos el nombre del computador/ip del servidor de compilación en la variable de entorno
DISTCC_HOSTS
(el orden es importante):export DISTCC_HOSTS="localhost red green blue"
-
Compilamos:
make -j8 CC=distcc
# O dependiendo de los lenguajes empleados # en el codigo fuente del proyecto... make -j8 CXX=distcc CC=distcc
Un ejemplo de un proyecto que usa C
y C++
como lenguajes de
implementación es el ide Geany.
-
Observa el comportamiento de los Servidores de compilación: Mientras reciben solicitudes de compilación sus núcleos están trabajando, al finalizar estas solicitudes, el uso de los núcleos y de la red decae.
Distcc (V). A tener en cuenta
-
En
DISTCC_HOSTS
colocamos por orden de preferencia o rapidez los servidores preferidos o más rápidos primero. Estos servidores también pueden estar en el archivo:$HOME/.distcc/hosts
-
En determinados S.O. la configuración del servidor al inicio suele estar en:
/etc/default/distcc
-
Es conveniente tener la misma versión del compilador en todas las máquinas de compilación. Sobre todo si trabajamos en C++ ya que el
ABI
puede cambiar, incluso entre versiones distintas de un mismo compilador.
¿Se puede integrar distcc
con ccache
?
-
Se puede, es sencillo ya que pueden funcionar como programas independientes que interactúan.
-
A tener en cuenta:
-
No usar en
distcc
el modo de bombeo (pump). Así ejecutamos el preprocesador una sola vez. -
Es mejor ejecutar
ccache
antes quedistcc
.
-
-
Basta con hacer una llamada al compilador así:
export CCACHE_PREFIX="distcc" CC="ccache gcc"
Enlace (linking).
-
Hemos hablado hasta aquí de tiempos de compilación, pero nada de tiempos de enlace y en ocasiones son muy grandes.
-
Si la compilación es paralelizable el enlace no…o no lo era hasta hace poco…
-
Disponemos de diversos enlazadores: ld y gold del proyecto GNU, lld del proyecto LLVM y más recientemente de mold.
-
Mold tiene, entre otras ventajas, el poder paralelizar el proceso de enlace. Echa un vistazo a este vídeo.
Parecido pero no-igual
-
GNU Parallel permite poner en marcha trabajos en paralelo.
-
Puedes ver el tutorial de uso en este enlace.
-
En la página de la wikipedia tienes ejemplos sencillos de uso.
-
Y en YouTube tienes vídeos de ejemplo.
Trabajo en grupo en clase
Prácticas individuales.
-
Bien con una práctica tuya, bien con un código descargado, comprueba tiempos de ejecución de la compilación con diversos valores para
N
en-jN
. ¿A partir de qué valores deN
ya no supone una mejora sustancial el incremento en el número de trabajos en paralelo? -
¿Hay fallos de compilación con ejecuciones en paralelo? Si es así trata de ver por qué se producen y procura solucionarlos.
-
Bien con una práctica tuya, bien con un código descargado, comprueba tiempos de ejecución de la compilación usando y sin usar
ccache
.
-
Configura un par de máquinas del laboratorio para aceptar solicitudes de
distcc
y bien con una práctica tuya, bien con un código descargado, comprueba tiempos de ejecución de la compilación al usardistcc
y comparalos cuando se compila todo el código en tu máquina.
-
En esta entrega solo tienes que incluir en una carpeta un archivo de texto explicando lo que has hecho y los resultados obtenidos con cada una de las herramientas anteriores. Esta carpeta comprimida en formato TGZ es la que deberás entregar.
-
Incluye en este archivo la URL del código fuente que has empleado con cada una de ellas. Usa un código fuente distinto al que tienes en los ejemplos vistos en clase.
Aclaraciones
-
Debes estudiar, aclarar y ampliar los conceptos que en ellas encuentres empleando los enlaces web y bibliografía recomendada que puedes consultar en la página web de la ficha de la asignatura y en la web propia de la asignatura.