motivación
La verdad, no sabía si mandar esta historia a Diario o intentar que llegue a la codiciada portada. Al final me decido por añadir enlaces a manta, cuidar un poco el estilo y enviarla a la cola de moderación. Así me lo curro un poco más.
Todo empezó con esta historia de Stallman: "La trampa de Java" [en castellano]. Stallman se queja de que por muy libre que sea tu programa en Java, la máquina virtual no lo es; así que de facto has caído en la trampa.
Por contra: el presidente de JBoss (creadores del contenedor de EJB's más popular, JBoss -- que encima viene con licencia GPL), Marc Fleury, empieza a largar contra el software libre. Le parece mala idea que Java (la implementación) sea liberado por Sun, y para disfrazarlo, parece que dice que se trata de la especificación. Irónico, ¿no? No me extraña, ya que Sun acaba de concederles la certificación J2EE; y es posible que en el trato con Sun haya más de lo que se publicó. Sun está haciendo muchos amigos últimamente; lástima que todos sean del mismo talante.
alternativa: gcj
Los hechos nos empujan, y en este caso mi reacción ha sido probar el software que sugiere Stallman: gcj
, o Gnu Compiler for Java. (Aquí hay una errata en su texto: aparece como gjc
.) Así que, armado con mi Mandrake, realizo los movimientos mágicos de varita:
# urpmi gcj-tools
# urpmi gcc-java
y a correr. Lo primero es intentar ejecutar tu código interpretado, usando gij
(puede usarse el enlace simbólico /usr/bin/java
). Lo flipé cuando mi programita scrollfeed, un clon del knewsticker, funciona perfectamente -- incluyendo mi librería de xml propia, y varias ventanas de SWT.
Pero lo más interesante viene de mano del propio gcc
, el compilador de GNU. Se puede usar el enlace simbólico gcj
, o incluso una simulación del comando de Sun /usr/bin/javac
.
Armado con esta herramienta, me decidí a intentar compilar algo de código Java a código nativo x86.
Pruebas de rendimiento
Hace algún tiempo, tuve la típica discusión con compañeros de trabajo sobre si Java era realmente tan lento, comparado con C a pelo. Esta vez la cosa nos llevó a hacer una comparativa estúpida, pero reveladora: sumar los n
primeros números. La primera prueba en C iba prácticamente igual de lenta que el código Java; pero a poco que se optimizara el código C para i586, la cosa empezaba a ser escandalosa: unas tres veces más rápido para el código optimizado. Por contra, con Java no se puede hacer nada; se supone que la máquina virtual (VM) se encarga de todo -- con escaso éxito.
Bien, la revancha de Java estaba al caer. Ejecuto un programita que suma los 1000000000 (10^9) primeros números, primero como enteros de 4 bytes (int
) y luego como enteros largos, de 8 bytes (long
). La prueba la realizo desde el entorno de ejecución de Eclipse y desde la línea de comandos.
Eclipse (int): 2.737 s
Eclipse (long): 5.128 s
java (int): 2.675 s
java (long): 5.086 s
Nada sorprendente: la suma de enteros de 4 bytes va casi el doble de rápido que usando 8 bytes, lo cual nos indica que estoy usando un procesador de 32 bits (jur, no me lo han cambiado por un Opteron mientras dormía). Además, la ejecución dentro del entorno de Eclipse es un pelín más lenta.
Ahora me lanzo a interpretar el código usando gij
. Las condiciones son las mismas.
gij (int): 91.276 s
gij (long): 171.414 s
No es muy alentador: va unas 35 veces más lento que la VM de Sun. Probamos ahora a compilarlo a código nativo. El comando a ejecutar (gcc
o gcj
) queda así:
$ gcj --main=Main src/Main.java -o Main.exe
lo que compila la clase Main
al programa Main.exe
(y perdonad el windowsismo). Como no necesita de más librerías, no es necesario ajustar el classpath. Luego se ejecuta como cualquier programa nativo. Los tiempos de ejecución son los siguientes:
gcj (int): 2.899 s
gcj (long): 14.691 s
La cosa pinta un poco mejor: casi tan rápido como la VM de Sun en enteros, y en enteros largos sigue siendo lentorra. Pero nos queda una esperanza: la legendaria capacidad de optimización de gcc
. Añado el parámetro -O2
, y vuelvo a ejecutar la prueba:
gcj (int): 1.003 s
gcj (long): 7.783 s
¡Más de dos veces más rápido que el código interpretado de Sun! (en 4 bytes, para enteros largos la cosa sigue siendo un poco peor, pero poco). Optimizaciones sucesivas (-O5
) consiguen disminuciones marginales en tiempo de ejecución. ¡Toma esto, C! Seguro que estamos ya en el rango de valores del C optimizado, y sería interesante comprobarlo.
He intentado otras cosas; suma de valores de coma flotante, llamadas a métodos, creación de objetos y demás, y los resultados son consistentes: un poco más lento que la VM de Sun, pero bastante usable. Además, parece que se pueden compilar librerías enteras (los famosos ficheros .jar
), pero ante cualquier error de compilación se echa atrás. Recordemos que sólo está oficialmente soportado el JDK 1.1, aunque muchas cosas del 1.2 (colecciones) vienen incluidas.
conclusión
Se puede tener código Java rápido, y sobre todo libre. Para conseguirlo, hay que armarse de paciencia y empezar desde abajo: compilando las librerías de utilidad, luego las de funcionalidad y finalmente el programa completo. El uso de librerías externas ralentiza el proceso, ya que hay que recompilarlas eliminando todo el código que no esté soportado.
Parece que el software libre nunca dejará de sorprendernos: lo que hemos probado aquí está, en algunos aspectos, a años luz de lo que ofrece Sun; con todo su entorno de compilación en tiempo de ejecución (JIT). Se pierde la supuesta portabilidad del código Java, pero se gana la (infinitamente más versátil) portabilidad de gcc
.
Sólo me queda animaros para que lo probéis, y colaboréis con gcj
aunque sea informando de los errores.