¿Por qué en Java imprimir "B" es más lento que "#"? Análisis con ejemplos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué "B" se ralentiza y "#" vuela? Descifrando la icónica pregunta de Java

\n\n

Si alguna vez has escrito bucles simples en Java para imprimir caracteres en la consola, quizás hayas notado algo extraño: la salida de la letra minúscula 'B' puede ser notablemente más lenta que la salida del símbolo de almohadilla '#'. Este efecto una vez explotó en Stack Overflow: la pregunta obtuvo miles de visitas y votos. Hoy analizaremos qué hay detrás de este fenómeno y cómo se relaciona con los mecanismos internos de Java, la entrada/salida de consola y el buffering.

\n\n

Planteamiento del experimento

\n

Imagina que ejecutamos dos bucles que imprimen 100,000 caracteres en la consola: un bucle imprime '#' y el otro imprime 'B'. A primera vista, la operación es idéntica. Sin embargo, en muchos sistemas (especialmente Windows), el primer bucle termina en ~100 ms, mientras que el segundo tarda varios segundos. ¿Por qué?

\n

Reproduzcamos esto en Java.

\n\n

Código para la prueba

\n
public class SlowPrintTest {\n    public static void main(String[] args) {\n        long start = System.nanoTime();\n        for (int i = 0; i < 100_000; i++) {\n            System.out.print("#");\n        }\n        long end = System.nanoTime();\n        System.out.println("\\nTiempo para '#': " + (end - start) / 1_000_000 + " ms");\n\n        start = System.nanoTime();\n        for (int i = 0; i < 100_000; i++) {\n            System.out.print("B");\n        }\n        end = System.nanoTime();\n        System.out.println("\\nTiempo para 'B': " + (end - start) / 1_000_000 + " ms");\n    }\n}
\n\n

Ejecuta este código en una terminal, no en un IDE (por ejemplo, mediante java SlowPrintTest). El resultado te sorprenderá. En Windows con CMD, la diferencia puede ser de 10 a 50 veces.

\n\n

¿Por qué sucede esto? La raíz del problema está en el renderizado de fuentes

\n

No es culpa de System.out.print() ni de la JVM. La razón principal es cómo el sistema operativo y la terminal procesan la salida de caracteres. System.out en Java es un envoltorio sobre el flujo de salida estándar (stdout). Los datos se envían a la consola, pero la velocidad de visualización en pantalla depende de:

\n
    \n
  • Ancho del carácter — muchas consolas antiguas (por ejemplo, CMD en Windows) usan fuentes monoespaciadas, pero el carácter 'B' puede requerir un renderizado más complejo que '#'.
  • \n
  • Actualización de pantalla — la consola puede redibujar completamente la línea al imprimir cada carácter, en lugar de simplemente agregarlo.
  • \n
  • Buffering de la terminal — algunas terminales almacenan la salida en bloques, pero si un carácter requiere calcular el ancho del glifo, esto ralentiza el proceso.
  • \n
\n\n

El efecto es más notorio en el Símbolo del sistema de Windows (CMD) con fuentes de mapa de bits. Cuando imprimes 'B', la consola intenta mostrar el carácter en modo gráfico, mientras que para '#' utiliza un enfoque simplificado. En terminales modernas (Windows Terminal, iTerm2, Alacritty), la diferencia puede ser menos perceptible o incluso inexistente.

\n\n

El papel del buffering en Java

\n

Un aspecto importante de la optimización de salida es el uso de flujos con buffer. System.out no tiene buffer por defecto (o mejor dicho, tiene buffer a nivel de JVM, pero con un tamaño pequeño). Cada llamada a print() puede invocar una llamada al sistema write(), lo cual es muy costoso. Si añadimos buffering mediante BufferedWriter, el rendimiento mejora, pero la diferencia entre caracteres persiste: está del lado de la terminal.

\n\n
// Ejemplo con buffering\nBufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));\nfor (int i = 0; i < 100_000; i++) {\n    bw.write("#");\n}\nbw.flush();
\n\n

Incluso con buffering, si la terminal renderiza lentamente 'B', el tiempo total aumentará. Una

Blogs

Book Recommendations