Si no puedes medirlo, no puedes mejorarlo

No hace tanto escribíamos sobre los (desastrosos) resultados de probar Ceibal-Chess sobre el Hardware real de la XO. Resultados debidos principalmente al bajo poder de cómputo de estas maquinitas en comparación con los equipos utilizados para desarrollar y probar el programa.

Equipo donde se desarrolló la mayoría del código: Core2Duo 2.1GHz vs. XO: AMD Geode a 500Mhz.

Finalmente hoy me dediqué a realizar algunas pruebas de profiling sobre el código, para intentar identificar los posibles cuellos de botella. Por profiling no me refiero a ninguna herramienta profesional más allá del clásico enfoque de combinar print’s con la función time() para ver por dónde va el código y medir el tiempo de ejecución de distintas llamadas.

Si bien se logró rastrear el principal cuello de botella al cálculo de movimientos, resultó que realizar estas mediciones arrojó unos resultados interesantes sobre posibles puntas para optimizar el programa. Principalmente llamó la atención la gran cantidad de veces que era llamada la función encargada de determinar si el rey se encontraba Jaque Mate. Detectar el Jaque Mate es la condición para determinar el fin de juego, una condición que debe testearse bastante seguido (en cada Frame) para presentar un mensaje, evitar que los jugadores puedan seguir moviendo piezas, etc.

Determinar si estamos en Jaque Mate involucra evaluar todos los posibles movimientos de las piezas del oponente (llamando a nuestra pesada get_moves()) para ver si el rey se encuentra contenido en alguno de ellos y si no tiene escape.

El abuso de esta función causaría que se dispararan un gran número de llamadas al cálculo de movimientos, lo cual consumía muchos ciclos. Este era un problema de Ceibal-Chess. De hecho, mediante las pruebas realizadas se logró detectar que la condición de Jaque Mate se revisaba incluso cuando el juego se encontraba desplegando el menú de opciones, algo completamente innecesario.

Mediante la eliminación de algunas llamadas superfluas, junto con la implementación de un sencillo cache de movimientos para la función que calcula todos los movimientos del oponente (get_all_oponent_moves()) fue suficiente para traer el desempeño del programa a un nivel aceptable sobre la XO.

Ciertamente el código requerido para solucionar el problema fué mucho más sencillo de implementar que si se hubiese tomado el enfoque de realizar la implementación del cálculo de movimientos en C++, lo cual si bien hubiese mejorado el desempeño, no hubiese la solución “natural”, y quizás no hubiese valido la pena por el nivel de complejidad que agrega al proyecto el tenerlo implementado de a partes en 2 lenguajes.

Moraleja de la historia: antes de salir a programar como locos, a veces es mejor sentarse a hacer algunas mediciones, por más que sientas que en ello “pierdes” el tiempo. Al final de cuentas, “si no puedes medirlo, no puedes mejorarlo”.

>> Sitio de Ceibal-Chess en Google Code: http://code.google.com/p/ceibal-chess

This entry was posted in ceibal-chess, Linux, Tweaking. Bookmark the permalink.