22 de febrero de 2017

Trying Chez Scheme

A new virtual machine for Racket

The plan is to replace the Racket virtual machine with Chez Scheme, including the interpreter and the JIT and everything else, within a year. There are still many unknowns and details to be solved. In particular, I'm interested in how Chez optimizations are compared to the optimizations Racket currently has. Moreover, there are a million small incompatibilities between Chez and Racket that must be fixed in the middle layer and they will directly or indirectly affect the optimizations.

The problem is that in order to understand what is going on, I need to break^H^H^H^H^H make modifications and maybe try to make improvements to the current optimizations in Chez, and see if the changes that I try are correct or they produce errors. So the first step is to compile Chez and take a look at the tests.


Compiling Chez on Windows

I can compile it, but I still have errors in the tests. I guess I have something misconfigured on my machine. I'm still fighting against iconv.dll. And also there is probably some other problem because there are too many tests that fail. Let's forget Windows for now.


Compiling Chez on Linux

Compiling on Linux was easier than I expected. Almost no surprises. Foolproof instructions for installing on a virtual machine:


  • Download and install VirtualBox
  • Download the Ubuntu disk (desktop)
  • Install Ubuntu on the virtual machine (not on the real machine). The only hard question is the size of the hard drive, I put 12GB and that is enough. I recommend turning off the option to lock the screen after 5 minutes of inactivity because several of the following steps take a long time.

The following steps are explained in the BUILDING file. But in case of doubt the recipe is:

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

The make test step performs a lot of tests and it takes about an hour, so you have to be patient. The errors will be shown at a6le/mats/summary and if there are no errors the file should be something like

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

Where each line represents a possible configuration of the tests. In my case it was a success (almost) at the first attempt.


Automatic tests in Travis

For the tests in Travis I wanted that in case of an error that the program finish with an error code other than 0, for Travis to notice it and signal the fault.

In addition, I wanted to be able to run each of the test configurations separately, so they can run in parallel in Travis. This also helps to identify where the error is.

One of the problems is that some of the variables of the makefile are reused.

o : In some makefiles it is obviously the extension of the files with object code, that is o=.o or o=.obj according to the compiler. In others makefiles it is obviously the level of optimization, typically o=0 or o=3.

cp0 : In some makefiles it is the number of times the step cp0 that performs the optimizations of the Chez code is applied, and in others it takes the values t or f to indicate whether the step cp0 is applied or not. (Maybe it’s possible to change the t/f by 0/1 (or 0/3) and use the same meaning everywhere?)

I solved it by creating two new variables one_o and one_cp0 that at some point are copied to the original variables, taking some care to handle the case where they are not defined.

To the main makefile I added an option to run all the tests with a given configuration instead of all the tests with all the configurations. For example:

make testone one_o=3 one_cp0=t

The changes in the makefiles are not very elegant, moreover, they are probably a monstrosity, but they run in my machine (be careful with the 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)

The .travis.yml file is more or less standard. Instead of make install I use make build to avoid using sudo, so Travis runs the tests almost immediately instead of waiting for amachine with sudo enabled. My version still generates a small complaint because build uses internally sudo or something like that, but it does not seem to be important for the final result.

.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

The code is available in github (tests ), along with an additional change that adds an obviously wrong test just to see that when there is an error it is detected (test ). 


Some ideas

My plan is to analyze the Chez code now, but there are some improvements that I think I will ignore for now.

I would like instead of running all tests for each option with make testone, being able to run each test separately, so the output in Travis is shorter and easier to understand and it is easier to find the error.

And I would like to try to fix the compilation in Windows and if possible add the tests in AppVeyor.

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.