22 de febrero de 2017

Probando Chez Scheme

Una nueva máquina virtual para Racket

El plan es que dentro de un año reemplazar la máquina virtual de Racket por la de Chez Scheme, incluyendo el intérprete y el JIT y todo lo demás. Todavía hay muchas incógnitas y detalles por resolver. A mí, en particular me interesa ver cómo son las optimizaciones que tiene Chez para poder compararas con las que tiene actualmente Racket. Además hay un millón de pequeñas incompatibilidades entre Chez y Racket que van a tener que ser arregladas en la capa intermedia y que directa o indirectamente van a afectar a las optimizaciones.

El problema es que para entender lo que está pasando necesito romper^H^H^H^H^H hacer modificaciones y quizás intentar hacer mejoras en las optimizaciones actuales de Chez y ver si los cambios que se me ocurren andan bien o producen errores. Así que el primer paso es compilar Chez y ver los testeos.

Compilando Chez en Windows

Lo pude compilar, pero todavía tengo errores en los test. Supongo que tengo algo mal configurado en mi máquina. Todavía sigo peleando contra iconv.dll. Y además probablemente haya algún otro problema más porque hay demasiados test que fallan. Olvidémonos de Windows por ahora.


Compilando Chez en Linux


La compilación en Linux fue más fácil de lo que esperaba. Casi sin sorpresas. Instrucciones a prueba de bobos para instalarlo en una máquina virtual:

  • Bajar e instalar VirtualBox
  • Bajar el disco de Ubuntu (desktop)
  • Instalar Ubuntu en la máquina virtual (no en a real). La única pregunta difícil es el tamaño del disco rígido, yo le puse 12GB y con eso alcanza bien. Recomiendo desactivar la opción de bloquear la pantalla a los 5 minutos de inactividad, porque varios de los siguientes pasos tardan mucho tiempo.

Los siguientes pasos están explicados en el archivo BUILDING. Pero en caso de dudas la receta es:

sudo apt-get install git
sudo apt-get install libncurses5-dev libncursesw5-dev
sudo apt-get install libx11-dev
git clone https://github.com/cisco/ChezScheme.git
cd ChezScheme
./ configure
sudo make install
make test
cat a6le/mats/summary

El paso make test realiza muchos testeos y tarda como una hora, así que hay que tener paciencia. Los errores van a parar a a6le/mats/summary y si no hay errores el archivo debería ser algo como

-------- o=0 --------
-------- o=3 --------
[...]
-------- o=3 ehc=t eval=interpret --------

En dónde cada línea representa una configuración posible de los testeos. En mi caso fue un éxito (casi) al primer intento.


Testeos automáticos en Travis


Para los testeos en Travis quería que si había un error el programa terminara con un código de error distinto de 0 para que Travis se diera cuenta y marcara el fallo.

Además, quería poder correr cada una de las configuraciones de los testeos por separado. Así se pueden correr en paralelo en Travis. Esto también ayuda a identificar dónde está el error.

Uno de los problemas es que algunas de las variables de los makefile se reutilizan. 

o : En algunos makefiles es obviamente la extensión de los archivos con código objeto, o sea o=.o ó o=.obj según el compilador. En otros makefiles es obviamente el nivel de optimización, típicamente o=0 ó o=3.

cp0 : En algunos makefiles es la cantidad de veces que se aplica el paso cp0 que realiza las optimizaciones del código de Chez y en otros toma valores t ó f para indicar si el paso cp0 se aplica o no. (¿Quizás se pueda cambiar el t/f por 0/10/3) y usar el mismo significado en todos lados?)

Lo solucioné creando dos variables nuevas one_o y one_cp0 que en algún momento se copian a las variables originales, teniendo un poco de cuidado para manejar el caso en que no están definidas.

Al archivo makefile principal le agregué una opción para correr todos los testeos con una dada configuración en vez de todos los testeos con todas las configuraciones. Por ejemplo: 

make testone one_o=3 one_cp0=t

Los cambios en los makefiles no son muy elegantes, es más, probablemente sean una monstruosidad, pero andan (ojo con los tabs):

makefiles/Makefile.in
testone:
(cd $(workarea) && $(MAKE) testone PREFIX=$(workarea)/)

makefiles/Makefile-workarea.in

ifdef one_o
  new_o = o=$(one_o)
else
  new_o = 
endif
ifdef one_cp0
  new_cp0 = cp0=$(one_cp0)
else
  new_cp0 = 
endif

testone: build

(cd mats ; $(MAKE) allxonewerror $(new_o) $(new_cp0))
@echo "test run complete.  check $(PREFIX)mats/summary for errors."

/mats/Mf-base

allxonewerror: prettyclean
$(MAKE) allxhelp
$(MAKE) maybe-raise-error

maybe-raise-error:

if [ "`wc -l < report-${conf}`" != "0"  ] ; then\
 $(MAKE) raise-error ;\
fi

raise-error:

$(error We found an error)

El archivo .travis.yml es más o menos estándar. En vez de make install uso make build para evitar usar sudo, así Travis ejecuta los test casi inmediatamente en vez de tener que esperar a que haya una máquina disponible con sudo habilitado. Mi versión todavía genera una pequeña queja porque build usa internamente sudo algo así, pero no parece ser importante para el resultado final.

.travis.yml 
sudo: false
language: c

matrix:

  include:
  - os: linux
    compiler: gcc
    env: CHEZ_TEST_ARGS="one_o=0"
  - os: linux
    compiler: gcc
    env: CHEZ_TEST_ARGS="one_o=3"
#[…]
  - os: linux
    compiler: gcc
    env: CHEZ_TEST_ARGS="one_o=3 eval=interpret ehc=t rmg=2"

script:

- ./configure
- make build
- make testone $CHEZ_TEST_ARGS

El código está disponible en github (tests ), junto con un cambio adicional que agrega un testeo obviamente erróneo sólo para ver que cuando hay un error se detecta (test ).


Algunas ideas:


Mi plan es ponerme a analizar el código de Chez directamente, pero quedan en el tintero algunas mejoras que creo que voy a ignorar por ahora. 

Me gustaría que en vez de ejecutar todos los test para cada opción con make testone, poder ejecutar cada test por separado, así la salida en Travis es más corta y fácil de entender y es más fácil encontrar el error.

Por otro lado, me gustaría tratar de arreglar la compilación en Windows y si es posible agregar los test en AppVeyor.