Aventuras en el evaluador, parte 1

Como parte de los propósitos de año nuevo, me dispuse a hacer un refactor masivo del código con el cual se evalúan y ejecutan las soluciones en omegaUp. Habían muchas motivaciones para hacer la actualización: tener un sandbox más moderno, eliminar un montón de código duplicado, mejorar las pruebas, el logging, el paralelismo en los jueces, cómo se despliega el tiempo y la memoria, el desempeño de los envíos, soportar C++11 y más lenguajes. Son muchísimos cambios, así que haré dos posts explicándolos: este post se centrará en los cambios que se hicieron en la arquitectura de jueceo y el siguiente se enfocará únicamente en el nuevo sandbox.

El primer cambio que se va a notar es que el servidor donde se hospeda la página principal ya no califica envíos! Gracias a nuestros queridos empleadores, conseguimos un par de máquinas virtuales gratuitas con Linux en Azure que se utilizarán exclusivamente para correr programas de los concursantes. Estas máquinas son menos poderosas que el servidor que usábamos antes, pero gracias a que el nuevo sandbox tiene muchísimo menos overhead, la diferencia será casi negligible. Una de las consecuencias de esto es que la página va a ser un poco más responsiva y los veredictos serán más rápidos. Esto también trae seguridad, porque aunque quisieran hacer trampa, ahora será casi imposible: las máquinas donde se ejecuta código de concursantes ya no son las mismas máquinas donde están las salidas esperadas :).

Otro cambio grande es la manera en la que se mide y reporta la memoria que consumen los programas. Antes la ejecución del programa se detenía cada cierto tiempo para poder medir exactamente cuántos bytes estaban siendo utilizados. Ahora para bajar el overhead del sandbox, la política de medición es mucho más laxa y sólamente se toman en cuenta los bytes de memoria privada del programa: esto excluye la librería de C estándar, el código ejecutable del programa, y algunas constantes. En Java, la manera en la que se establece el límite de memoria no cambió (así que cualquier envío que fuera MLE antes lo seguirá siendo), pero ahora para que se vea más bonito el reporte, se excluye de la cuenta la memoria ocupada por la máquina virtual de Java. Hablando de Java, la causa #1 de errores de compilación antes se reportaba como Runtime Error: usar un nombre de clase que no es “Main”. Ahora se reporta como error de compilación y se te indica que la clase se debe llamar Main.

Todos los runtimes y compiladores fueron actualizados. Ahora usamos GNU GCC 4.8, Java OpenJDK 7.0, Free Pascal 2.6, Ruby 1.9, Python 2.7, Glorious Glasgow Haskell Compiler 7.6 y Karel 1.1. Soportamos ahora sí todos los features de Java 7 (bienvenido sea el operador diamante), y agregamos la opción para compilar en C++11 (posiblemente en un futuro eliminemos la versión anterior de C++, gnu++03, cuando el soporte de C++11 sea lo suficientemente maduro).

El bug más feo que se arregló fue uno en el cual si habían más de 2 jueces conectados, únicamente se usaban dos, desperdiciando mucho tiempo y poder de cómputo. Esto únicamente ocurría durante concursos muy grandes, pero era bastante molesto para nosotros. También habían muchos errores con el manejo de la cola de espera, tanto de los jueces para poder compilar envíos, como la de los envíos que esperan un juez disponible. El resultado es que donde antes habían esperas de varios segundos, ahora aún en periodos donde la cola está llena las máquinas que juecean estarán esperando únicamente ~20 milisegundos entre envíos en lo que se les asigna algo para hacer.

Después de aproximadamente 80 commits en nuestros varios repositorios, espero que disfruten nuestra nueva y mejorada infraestructura (que en realidad debería ser totalmente invisible si hice bien mi trabajo).