miércoles, 14 de mayo de 2014

Cómo [no] ayudar al compilador

Siempre les digo a mis alumnos: "el compilador es más vivo que todos nosotros juntos", y la experiencia me ha demostrado que es una buena máxima para respetar a la hora de programar. Me refiero a esos momentos donde programamos algo de cierto modo que no es precisamente el más prolijo ni el más simple, pero imaginamos que hacerlo como dicen los libros sería agregar algún tipo de sobrecarga evitable. Y esto se debe a que imaginamos mal o desconocemos qué va a hacer con eso el compilador.

Por dar un ejemplo simple, he usado mil veces macros de preprocesador (#define func(x) bla...bla) en lugar de funciones para evitar la sobrecarga que significa invocar a una función. Está claro que a nivel ensamblador la llamada a una función, por simple que sea ésta, implica varias instrucciones adicionales (preparar el stack, crear variables locales, pasar los argumentos, recibir el resultado, etc). Pero debería estar igual de claro que considerar eso al codificar es un gran gran error. Es un problema que un buen compilador sabrá resolver mucho mejor que nosotros. En este post quiero dejar algunos tips sobre cosas como estas no hay que hacer y otras  cosas sí, si queremos que lo que hacemos de verdad le sirva al compilador para generar un mejor ejecutable.

lunes, 5 de mayo de 2014

Interesante problema de Estructuras de Datos

En varios casos en ZinjaI tengo datos que necesito utilizar en dos contextos diferentes, requiriendo en uno el conjunto completo, y en otro solo una parte. Tomemos por ejemplo la lista de breakpoints de un proyecto. Mientras se edita un archivo fuente, la mayoría de las operaciones con breakpoints (agregar, quitar, definir propiedades, etc) se hará sobre los de ese fuente. Además, al guardar esta información en el proyecto, los breakpoints de un fuente se guardan asociados al mismo. Sin embargo, una lista de breakpoints por fuente no siempre es lo mejor porque muchas operaciones en el depurador (a nivel gdb) se harán sobre la lista completa. Por ejemplo, al inicializar la depuración, se deben setear todos los breakpoints en gdb antes de ejecutar, sin importar de dónde provengan. La misma idea aplica a varias otras cosas, como por ejemplo los archivos del proyecto (que están agrupados por categorías) o próximamente las inspecciones (que están asociadas a distintas ventanas/paneles de la interfaz).

Entonces, hace poco me armé un par de clases para representar dos contenedores que operen sobre los mismos elementos. Es decir, habrá un contenedor para el conjunto global, y otro para los subconjuntos parciales. A nivel algoritmos y estructuras de datos creo que es un ejercicio interesante, y por eso vengo a (documentarlo) comentarlo  en este post.