Libertonia
Portada · Todo · Software Libre · Desarrolladores · Comunidad · Internet · Tecnología · Meta · Diarios
Compilando código Java

Programación
Por man ls
departamento Java libre , Sección Software Libre
Puesto a las Mon May 10th, 2004 at 06:12:09 PM CET
Ahora que Sun y Microsoft han encontrado el amor verdadero (ya se sabe que la distancia entre el odio y el amor es más delgada que un papel de fumar), los partidarios de la liberación del lenguaje Java lo tenemos mal. Lo pidió Eric Raymond, después IBM secundó la moción; pero ahora el momento parece cada vez más lejano. ¿Hay alguna opción?

En este artículo realizo una comparativa entre la máquina virtual de Sun y la máquina no-virtual de GNU. Sí, amigos, el gcj funciona: compila código Java a código nativo; y yo soy el primero que lo flipa.

 


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.

< Bacula el vampiro (y III) (1 comments) | Jornadas de Redes Telecomunicaciones Públicas (2 comments) >
Enlaces Relacionados
· Sun y Microsoft han encontrado el amor verdadero
· Lo pidió Eric Raymond
· IBM secundó la moción
· esta historia de Stallman
· Marc Fleury, empieza a largar contra el software libre
· gcj, o Gnu Compiler for Java
· More on Programación
· Also by man ls

Encuesta
¿Vas a probar gcj?
· Sí, libre mola más 28%
· Según te portes 13%
· No, uso Sun-VM 9%
· No, uso IBM-VM 0%
· No, uso blackdown 2%
· No uso Java 19%
· No me gusta Java 19%
· Sólo uso Rubia-VM 6%

Votos: 87
Resultados | Otras Encuestas

Menu
· crear cuenta
· FAQ
· búsqueda
· Fuentes de Noticias

Login
Nueva cuenta
Usuario:
Contraseña:

Ver: Modo: Orden:
Compilando código Java | 15 comentarios (15 temáticos, editoriales, 0 ocultos)
Resultados más completos (5.00 / 1) (#1)
por man ls a las Tue May 11th, 2004 at 01:37:02 AM CET
(Información Usuario)

Para daros una visión más general del asunto, he hecho una comparativa un poco más a fondo entre la máquina virtual de Sun y el código compilado por gcj. Lo pongo en un comentario, para no distraer de la historia principal.

La primera comparativa es la "null": ejecuta los métodos de prueba pero sin nada dentro. Luego se sustrae a cada prueba el tiempo de esta primera. Todos los tiempos en segundos. Se miden siempre 1000000000 (10^9) iteraciones, excepto en append string donde sólo se realizan 200000 (2*10^5) (porque iba lentísimo en ambos).

El código de Sun se ejecuta así:
$ java -classpath "bin" Benchmarker
y el de gcj:
$ gcj -O5 --main=Benchmarker src/*.java -o benchmark
$ benchmark
results for null benchmark
java        gcj
0.812       1.527

results for add int benchmark
java        gcj
2.66        0.082

results for add float benchmark
java        gcj
3.703       0.981

results for add double benchmark
java        gcj
2.649       0.985

results for method call benchmark
java        gcj
2.818       0.081

results for create object benchmark
java        gcj
3.081       10.02

results for append string benchmark, 1/5000
java        gcj
4.003       9.583


Mi impresión general es que gcj se porta de maravilla con las operaciones aritméticas y las llamadas a métodos. Sin embargo, la creación de objetos -- y, sobre todo, la recolección automática -- le cuesta más. En general, es bastante impresionante, considerando los años y el esfuerzo que ha dedicado Sun a optimizar tanto el intérprete como el compilador HotSpot.

Máquina de prueba: PIV a 2.4 GHz, 512 MB de RAM, Mandrake 9.2, disco duro a manta. JDK 1.4.2 (HotSpot), gcj 3.3.2. Suministro el código de prueba a cambio de un foskito.



Preguntador-P-Pregunta (none / 0) (#10)
por jcantero (jcantero@agujero-negro.escomposlinux.org) a las Wed May 12th, 2004 at 09:05:47 AM CET
(Información Usuario) http://www.escomposlinux.org/jcantero/

Sin embargo, la creación de objetos -- y, sobre todo, la recolección automática -- le cuesta más.
Hmmmm, por curiosidad, ¿has forzado la recolección de basura en las pruebas?

--
"Papá, ¡Internet es más que una red pornográfica global!" -- Lisa Simpson
[ Padre ]


No, no he llamado al recolector (none / 0) (#11)
por man ls a las Wed May 12th, 2004 at 01:29:03 PM CET
(Información Usuario)

Te refieres a la llamada System.gc(), ¿no? Lo malo es que sólo es un hint a la máquina virtual: puede pasar de ti, o puede darle por recoger objetos. No sé cómo se portará en el gcj.

También he visto una opción curiosa: usar el famoso recolector de Boehm, que ya me he encontrado mencionado en varios sitios. Debe ser la caña, y tiene que ser interesante compararlo con el que viene por defecto. Me lo apunto: forzar la recolección, y probar con el nuevo recolector de Boehm.

[ Padre ]


Sí, System.gc() (none / 0) (#14)
por jcantero (jcantero@agujero-negro.escomposlinux.org) a las Fri May 14th, 2004 at 09:51:53 AM CET
(Información Usuario) http://www.escomposlinux.org/jcantero/

Ségun el libro de Bruce Eckel(*):
Si se invoca a System.gc() se finalizan todos los objetos. Esto no era necesario en el caso de las implementaciones previas de del JDK, aunque la documentación decía otra cosa.
(*) Lo he sacado de la edición en castellano (Piensa en Java), que corresponde con la 2ª edición en inglés. En ella se refiere principalmente a JDK 1.2 y 1.3.

--
"Papá, ¡Internet es más que una red pornográfica global!" -- Lisa Simpson
[ Padre ]


Ya lo he probado (none / 0) (#15)
por man ls a las Sat May 15th, 2004 at 05:48:45 PM CET
(Información Usuario)

Y no hay diferencia apreciable, entre crear n objetos y crear n objetos + llamar al recolector.

Tampoco hay diferencia apreciable usando el recolector de Boehm:
gcj [...] -fuse-boehm-gc
Vamos, que el tiempo de recolección no afecta mucho.

[ Padre ]


 
Pueeeeesss.... (4.00 / 1) (#2)
por thuban a las Tue May 11th, 2004 at 09:47:26 AM CET
(Información Usuario)

¿No es un poco decepcionante? Si, esta bien que haya un compilador y un interprete libre, pero ¿creeis que alguien va a usar una maquina virtual que es 35 veces mas lenta que la maquina de Sun? Recordemos que los de Sun son los que inventaron el lenguaje, los que dictan el estandar... En fin. La apuesta segura. Y ademas, es gratis (no se de nadie que haya tenido que pagar por el) tambien.

Puede hacerse que vaya mas deprisa, pero para eso hay que compilarlo (y perder la portabilidad).

A mi se me ocurren tres escenarios en los que se usa java:

* aplicaciones que se escriben una vez con la idea de que se ejecuten en cualquier sitio.

* servidores de JSPs, EJBs y cosas de esas.

* cualquier cosa que se escribe en Java por la sencilla razon de que es lo que sabe el que lo programa.

En el tercer caso no hay mucho que discutir. Se usa Java porque no hay mas remedio. Punto.

En el primero, la ventaja principal es que es portable el binario. Por ejemplo, JEdit es un editor que nos viene muy bien a los que tenemos Linux y Windows en la misma maquina (o en otra que usamos a menudo, como la del trabajo) porque usamos la misma aplicacion en cualquier sitio.

Si se usa gij la aplicacion sera ¡35 veces mas lenta! (o mas, o menos, pero mas lenta). Si se compila para que sea casi ¡el triple! de rapida, se pierde la portabilidad, a parte de que podrian dejar de funcionar cosas como carga dinamica de clases (hablo por hablar, que no lo he probado).

Se pierde la ventaja de usar Java para estas aplicaciones.

En el caso de los servidores empresariales, esos que sirven JSPs, EJBs y similares, no estoy seguro de que funcionen sobre gij, y si lo hacen, como el propio servidor hace compilacion dinamica, pooles de objetos, creacion dinamica y mas cosas de esas que estan tan cerca del lenguaje, es mas que probable que no funcionen bien.

Si se usa el interprete ira muuuucho mas lento, que es algo que en la web no se puede permitir.

Resumiendo: ¿Quien va a preferir el gij al java de Sun?

A mi se me ocurre que en proyectos libres donde se tenga acceso a las fuentes (hay muchas aplicaciones Java por ahi que no tienen accesibles las fuentes) donde no haya que compartir las clases entre varios sistemas operativos tiene mucho sentido hacer el ejecutable nativo (¿cuanto tardara la Gentoo en tener esto?), pero en el caso general, no.





Depende de las ganas que le pongas (none / 0) (#6)
por man ls a las Tue May 11th, 2004 at 09:25:44 PM CET
(Información Usuario)

Hace un par de años, un tipo contaba en la lista de Tomcat que lo había portado a C. Sí, C puro. También hay una versión de Axis en C++. Está claro que se puede programar lo que sea, pero se hace o no según el interés que se tenga.

Además, también puede haber otras ventajas, como el consumo de memoria. Es posible que, en tu hardware, el uso de Java sea prohibitivo; pero que una vez compilado sea más que aceptable.

No sé leer muy bien la salida de top, pero he hecho unas pruebecillas con mi librería de xml. Parseo un fichero más o menos grande, y luego la dejo esperando. El resultado es como sigue:
        VIRT   RES   SHR
java    205m   11m   39m
gcj    24400   11m   10m
Parece que consume muchísima menos memoria -- aunque la residente sea la misma. La verdad, no sé muy bien qué significa esto.

Personalmente, creo que gcj es una opción interesante a considerar.

[ Padre ]


significado (none / 0) (#13)
por musg0 a las Thu May 13th, 2004 at 03:33:32 PM CET
(Información Usuario) http://helvete.escomposlinux.org

Si no me equivoco RES es la memoria que está chupando la aplicación por sí misma, mientras que SHR es la que chupa de librerías compartidas que pueden ser usadas por más aplicaciones.

Que gcj tenga menos shared puede significar que como sólo soporta Java 1.1 las librerías dinámicas que usa tienen menos código.

[ Padre ]


 
Otras opciones (none / 0) (#3)
por ch3m4 a las Tue May 11th, 2004 at 10:39:00 AM CET
(Información Usuario) http://zaralinux.org

Si se habla de alternativas a la máquina virtual java, vale la pena incluir también el mono y el portablenet (dotGNU). Microsoft ofrece compiladores y "traductores" para pasar código java al entorno .Net , pero en el lado libre existe un proyecto para compilar java a CIL (http://jilc.sourceforge.net/)

En otro orden de cosas, pensaba que el JBoss no tenía la certificación J2EE. Sun ofrece becas para que proyectos de software libre obtengan la certificación, y a JBoss no le han ofrecido estas becas por ser un proyecto perteneciente a una empresa. El único proyecto libre que conozco que ha conseguido una certificación J2EE con estas becas era JOnAS del ObjectWeb.



Pues se ve que sí (none / 0) (#4)
por man ls a las Tue May 11th, 2004 at 11:34:05 AM CET
(Información Usuario)

Como cuentan en esta nota de prensa (lo siento, tienen la mala costumbre de ponerlas como pdf), no sólo están certificándose para J2EE 1.4, sino que además van a participar en el grupo experto que define la especificación de EJBs. Se ve que, tras años de llorar para obtener la certificación gratis, los de JBoss se han rascado el bolsillo para pagar la tarifa completa.

[ Padre ]


 
¿-O5? (none / 0) (#5)
por coder a las Tue May 11th, 2004 at 08:46:24 PM CET
(Información Usuario) http://fluzo.org/

Creo que esa opción no hace más que -O3. Que yo sepa GCC no optimiza más allá de -O3 (y haciendo demasiado "inlining", lo cual no es bueno). Todo lo que pongas a partir de -O4 (incluido) no tiene efecto sobre -O3. ¿O ha cambiado la cosa? ¿GCC 3.4?
atrapado por tu moda


Sigue igual (none / 0) (#7)
por ridiculum a las Wed May 12th, 2004 at 12:44:43 AM CET
(Información Usuario)

Segun la documentacion del gcc 3.3.3 y de gcc 3.4, la cosa sigue igual. Me suena vagamente, que en algun lugar de los fuentes del gcc se hace algo como :

if (flag_optimice > 3)
  flag_optimice = 3;
(evidentemente, no es exactamente asi, pero la idea se entenderá)

[ Padre ]


Sí, es así (4.00 / 1) (#12)
por jorginius ("jorginius" en Google Mail) a las Wed May 12th, 2004 at 05:34:07 PM CET
(Información Usuario) http://www.rodriguezmoreno.com

O casi, en realidad es un "if(optimize>=3)", aunque luego no hay un "if(optimize>=4)".

Por si alguien quiere echarle un ojo, aquí está la referencia cvsweb de opt.c de GCC.

Sobre lo que comenta coder de que con "-O3", gcc hace "demasiado inlining" sólo decir que es que con "-O2" el compilador no hace a ninguna función inline (ni muchas ni pocas :-)), a menos que la hayas marcado explícitamente con la extensión de GNU o de C99 (o la de C++, claro), y sin flag de optimización ("-O0") ni eso.

De hecho, esa es una de las razones por las que el kernel se compila con "-O2", evitar los inline para facilitar la depuración y los volcados de los errores.

Por otra parte, el "limite de complejidad" de lo que el compilador hará o no hará inline por su cuenta y riesgo cuando usemos "-O3", es configurable desde la línea de órdenes.

[ Padre ]


 
Se nota que soy un novato (none / 0) (#8)
por man ls a las Wed May 12th, 2004 at 12:53:50 AM CET
(Información Usuario)

Mi idea era optimizar el código al máximo, y no sabía cuál era el tope. Gracias, ya lo sé para la próxima.

[ Padre ]


En realidad lo has hecho bien (none / 0) (#9)
por jcantero (jcantero@agujero-negro.escomposlinux.org) a las Wed May 12th, 2004 at 08:48:08 AM CET
(Información Usuario) http://www.escomposlinux.org/jcantero/

Como podrás observar en muchos makefiles genéricos, se compila con optimizaciones al máximo (-O6) y luego es el compilador el que hace "el descuento". ;-)

Claro, eso en programas en los que sepamos que la optimización no afecta su funcionamiento.

--
"Papá, ¡Internet es más que una red pornográfica global!" -- Lisa Simpson
[ Padre ]


 
Compilando código Java | 15 comentarios (15 temáticos, editoriales, 0 ocultos)
Ver: Modo: Orden:

ecol Logo Powered by Scoop
Todas las Marcas Registradas y copyrights de esta página son propiedad de sus respectivos dueños.
Los comentarios son propiedad del que los escribe.
Los iconos de las noticias y el logotipo son propiedad de Javier Malonda.
El Resto © 2002 Escomposlinux.org y aledaños.

Puedes sindicar los contenidos de libertonia en formato RSS 1.0 y RDF 0.9. También se puede sindicar la cola de envíos pendientes de moderación.

El proyecto escomposlinux.org está dedicado a la memoria de tas

crear cuenta | faq | búsqueda