poniedziałek, 9 czerwca 2008

Eclipse Ganymede i kompilowanie javy do kodu natywnego

Czołem!

Zapewne zauważyłeś na kilku blogach wysyp wpisów o nowej wersji środowiska Eclipse Ganymede. Dzieje się tak dla tego, że jesteśmy łasi na koszulki, które Eclipse obiecuje w konkursie Ganymede Around the World Contest. Nie inaczej jest ze mną. Zobaczymy, co z tego wyjdzie.

Tak więc dziś postanowiłem się zmierzyć z tematem, który czasami pojawia się na łamach grupy dyskusyjnej pl.comp.lang.java i który zawsze uważałem za ślepą uliczkę rozwoju tego języka. Chodzi o kompilację kodu źródłowego Javy do kodu natywnego systemu operacyjnego.

Plusy tego rozwiązania:
- nie potrzeba JRE
- wyższa wydajność

Minusy takiego rozwiązania:
- brak przenośności
- niższa wydajność

O ile brak konieczności instalacji JRE jak i tracona przenośność są raczej bezsprzeczne o tyle do rozstrzygnięcia pozostaje kwestia wydajności, którą się dzisiaj zajmę.

Kod źródłowy będę pisał oczywiście przy pomocy najnowszego Eclipse Ganymede. Uruchamiał i kompilował będę javą 1.6. Kompilację do kodu natywnego przeprowadzę z wykorzystaniem gcj 4.2.3.

Najpierw zainstaluję gcj. Mam Ubuntu, więc wystarczy:
mateusz@mateusz-laptop:~$ sudo apt-get install gcj

sprawdzenie co mam zainstalowane:

mateusz@mateusz-laptop:~$ java -version
java version "1.6.0_06"
Java(TM) SE Runtime Environment (build 1.6.0_06-b02)
Java HotSpot(TM) Server VM (build 10.0-b22, mixed mode)
mateusz@mateusz-laptop:~$ gcj --version
gcj (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu6)
Copyright (C) 2007 Free Software Foundation, Inc.
To jest wolne/otwarte oprogramowanie; zobacz kod źródłowy aby zapoznać sie
z warunkami kopiowania. Nie ma ŻADNEJ gwarancji; nawet na MOŻLIWOŚĆ SPRZEDAŻY
ani na ODPOWIEDNIŚĆ DO KONKRETNEGO CELU.

Pobieram też Eclipse Ganymede.
Przed uruchomieniem tworzę skrót w pasku szybkiego uruchamiania i tu niespodzianka... W archiwum eclipse-jee-ganymede-RC2-linux-gtk.tar.gz Nie ma ikonki Eclipse, albo ja nie umiem jej znaleźć. Posłużę się ikonką eclipse.xpm z wersji 3.3 tego środowiska.

Odpalam Eclipse:



Niespodzianka numer 2:



Eclipse chce wysyłać informacje o moich wtyczkach. Przez chwile czuję się jak na Windowsie, po czym zaznaczam opcję, żeby nic nie wysyłał.
Tworzę projekt native i klasę pl.matt.gcj.TestClass


package pl.matt.gcj;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* @author mateusz
*
*/
public class TestClass {

/**
* @param args
*/
public static void main(String[] args) {
long start = System.currentTimeMillis();
List list = new ArrayList();
for (byte i = 0; i < 300; i++) {
list.add(list.size());
if (list.size() > 10000000) {
break;
}
}

Collections.shuffle(list);
double avg = 0;

for (Integer integer : list) {
avg += (double)integer/list.size();
}

long stop = System.currentTimeMillis();
System.out.println(stop-start + " ms.");
}

}

Do listy wstawiam 10000001 elementów. Listę mieszam i liczę średnią. Używam automatycznej konwersji int do Integer (autoboxng). GCJ podobno radzi sobie z kodem źródłowym zgodnym z Javą 1.5. Zobaczymy.
Kompiluję kod za pomocą gcj:

mateusz@mateusz-laptop:~/priv/workspace/native/src$ gcj -o /tmp/gcj/a.out pl/matt/gcj/TestClass.java
/usr/lib/gcc/i486-linux-gnu/4.2.3/../../../../lib/crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: ld returned 1 exit status

rzut okiem na FAQ GCJ i już wiem, że trzeba podać położenie metody main()

mateusz@mateusz-laptop:~/priv/workspace/native/src$ gcj --main=pl.matt.gcj.TestClass
-o /tmp/gcj/a.out pl/matt/gcj/TestClass.java

poszło... Został utworzony plik /tmp/gcj/a.out
Czas na testy. Ponieważ czas wykonywania programu w Javie nie uwzględnia czasu uruchomienia samej wirtualnej maszyny Java, tworzę 2 proste skrypty w BASHu:
a.sh który 5 razy uruchomi plik zawierający kod natywny a.out

mateusz@mateusz-laptop:/tmp/gcj$ cat a.sh
#!/bin/bash
START=$(date +%s)
./a.out
./a.out
./a.out
./a.out
./a.out
STOP=$(date +%s)
let T=$STOP-$START
echo $T " sekund"

oraz j.sh, który uruchomi TestClass za pomocą VM Java.

mateusz@mateusz-laptop:/tmp/gcj$ cat j.sh
#!/bin/bash
START=$(date +%s)
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
STOP=$(date +%s)
let T=$STOP-$START
echo $T " sekund"

Testy nie pozostawiają wątpliwości:

mateusz@mateusz-laptop:/tmp/gcj$ ./a.sh
8630 ms.
8299 ms.
8290 ms.
8380 ms.
8321 ms.
43 sekund
mateusz@mateusz-laptop:/tmp/gcj$ ./j.sh
5671 ms.
5824 ms.
5913 ms.
5906 ms.
5823 ms.
29 sekund

Co ciekawe czas uruchomienia wirtualnej maszyny Java jest praktycznie pomijalny.
Reasumując:

Plusy kompilowania aplikacji Java do kodu natywnego
- nie potrzeba JRE

Minusy takiego rozwiązania:
- brak przenośności
- niższa wydajność

A może ty stosujesz na co dzień kompilację programów napisanych w języku Java do kodu natywnego? Jeżeli tak, napisz dlaczego i jakie są tego zalety, których nie widzę.

2 komentarze:

a pisze...

Bardzo ciekawy test :) - oby takich więcej

Racjonalny Developer

Anonimowy pisze...

A kompilowales z opcja -O2? Bo domyslnie jest bez optymalizacji...