Cómo aprendí a leer el rendimiento de mi código leyendo primero mis propios hábitos

Este artículo puede incluir enlaces de afiliado. Si compras a través de ellos, me llevo una pequeña comisión sin coste extra para ti. Más información.

El problema no era el código. Bueno, sí era el código, pero el código era una consecuencia. La causa real era que me había sentado a escribir sin pensar, y eso lo hago más a menudo de lo que me gusta admitir.

Hace unos meses estaba trabajando en una utilidad pequeña para contar palabras en documentos de texto. Nada del otro mundo. El tipo de tarea que uno asume que resolverá en veinte minutos y que acaba convirtiéndose en dos horas de vergüenza silenciosa frente al monitor. Kika, una de mis gatas — se quedó dormida encima del teclado numérico. Yo seguí picando código a su lado como si supiera lo que estaba haciendo. No lo sabía.

Lo que hacía el código y lo que creía que hacía

La primera versión era honesta en su torpeza. Leía un archivo línea a línea, hacía un split(" ") sobre cada línea, metía cada palabra en un HashMap<String, Integer> y actualizaba el contador. Funcionaba. Los tests pasaban. Me fui a cenar tranquilo.

El problema apareció cuando alguien me pasó un archivo de verdad. No el típico Lorem Ipsum de tres párrafos, sino un documento de unos cuantos cientos de miles de palabras. El programa tardaba. Mucho. El tipo de tardanza que hace que te preguntes si has cometido un error existencial.

Metí el profiler. Y ahí estaba: estaba creando objetos String sin parar. Cada split() generaba un array nuevo. Cada concatenación menor que había metido por pereza creaba otra cadena temporal. El garbage collector trabajaba más que yo. Y yo estaba bastante ocupado sintiéndome mal.

La solución técnica no fue tan complicada una vez que la vi. Cambié el split() por un StringTokenizer, luego exploré usar expresiones regulares precompiladas con Pattern.compile() fuera del bucle, y finalmente envolví la lectura en un BufferedReader con un tamaño de buffer decente. La diferencia fue notable. Pero lo que me quedó dando vueltas no fue el resultado, sino el proceso que me llevó hasta el problema.

El patrón mental que precede al código malo

Cuando me siento a escribir un libro —llevo dos sobre gatos, ninguno sobre optimización de software, aunque quizás debería planteármelo— tengo una fase que llamo «el borrador de la deshonestidad». Es cuando escribo deprisa, sin estructura, solo para tener algo en la pantalla. Sirve. Es necesario. Pero sé que estoy en ese modo.

Con el código no tenía esa conciencia. Me sentaba a programar convencido de que estaba siendo productivo, cuando en realidad estaba en el equivalente al borrador de la deshonestidad: tomando decisiones sin pensar en sus consecuencias porque quería ver resultados rápido.

El split(" ") en lugar de algo más robusto. El HashMap sin pensar en la capacidad inicial. La lectura sin buffer porque «tampoco iba a ser un archivo tan grande». Cada una de esas decisiones era una pequeña rendición a la comodidad del momento.

Lo curioso es que si alguien me hubiera preguntado en ese momento si sabía lo que estaba haciendo, habría dicho que sí. Con convicción, además.

El autoengaño de la fluidez

Hay un estado al programar que se parece mucho al flow pero que no lo es. En el flow real tomas buenas decisiones rápido. En el otro estado —que no tiene nombre oficial pero que todos hemos vivido— tomas decisiones rápido y ya está. La velocidad se siente igual. El resultado no.

Lo que me delató no fue el profiler. Fue que cuando lo abrí y vi los resultados, no me sorprendí tanto como debería. En algún lugar sabía que había algo raro. Lo había notado mientras escribía y había decidido seguir adelante igualmente. Eso es lo que me pareció más revelador.

Reconocer el momento antes de que ocurra

Desde entonces tengo una señal de alarma personal bastante tonta pero efectiva: cuando estoy escribiendo código y pienso «esto ya lo arreglo luego», paro. No siempre. Pero intento hacerlo. Porque «ya lo arreglo luego» es exactamente lo que pensé con el split(), y «luego» llegó en forma de dos horas con el profiler y una sensación desagradable.

No es que haya dejado de escribir borradores de código. Sigo haciéndolo. Pero ahora intento ser consciente de que estoy en ese modo, igual que cuando escribo prosa. La diferencia entre un borrador que sabes que es un borrador y uno que crees que es la versión final es enorme.

Lo que el profiler te dice y lo que no puede decirte

Leí hace poco un artículo de Elias Mohammadi donde detallaba cómo optimizó un contador de palabras en Java y consiguió multiplicar por siete el rendimiento. Las técnicas que mencionaba eran sólidas: evitar instanciaciones innecesarias, usar estructuras de datos con capacidad preinicializada, minimizar el trabajo dentro de los bucles. Todo correcto. Todo útil.

Pero lo que un profiler no te va a decir es por qué llegaste hasta ahí. Te muestra dónde está el fuego, no por qué lo prendiste.

La optimización técnica es la parte fácil, en cierto modo. Hay documentación. Hay benchmarks. Hay personas que han medido qué es más rápido y por cuánto. Lo que no está documentado es el proceso interno que te lleva a escribir un bucle con diez creaciones de objetos innecesarias cuando podrías haber escrito uno limpio desde el principio.

Y ese proceso, al menos en mi caso, tiene más que ver con el estado mental en que me siento a trabajar que con el conocimiento técnico que tengo disponible.

Qué cambió después de aquello

Poco. Y mucho. Según se mire.

Sigo cometiendo errores de rendimiento. Sigo escribiendo código que luego necesita revisión. Pero ahora hay una pequeña pausa antes de ciertas decisiones que antes no existía. Una fracción de segundo en la que me pregunto si estoy tomando este camino porque es el correcto o porque es el que tengo más a mano.

Oli, otro de mis gatos, tiene la costumbre de sentarse justo delante de la pantalla cuando lleva un rato sin recibir atención. Es un sistema de alertas más efectivo que cualquier profiler. Cuando lleva ahí cinco minutos y no lo he movido, generalmente es porque estoy en el modo de escribir deprisa sin pensar.

A veces los mejores debuggers no son herramientas. Son interrupciones.


Inspirado parcialmente en: From Strings to Silicon: How I Optimized a Java Word Count by 7X

Foto: a person is typing on a computer keyboard por DFY® 디에프와이 en Unsplash.

¿Quieres más?

Recibe los nuevos artículos sobre gatos, escritura y código. Sin frecuencia fija, sin paja de IA, sin spam.

Suscribirme

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio