Obsah
1. Pohled pod kapotu JVM – přednosti a zápory využití JNI při optimalizacích (3)
2. Benchmark měřící rychlost výpočtu průměrné hodnoty všech prvků v poli
3. Překlad a spuštění benchmarku
4. Výsledky prvního benchmarku
5. Strojový kód generovaný JIT překladačem
6. Strojový kód generovaný překladačem GCC
7. Částečné řešení problému při sdílení polí – využití regionů
8. Benchmark pracující pouze s částí sdíleného pole
9. Překlad a spuštění benchmarku
11. Repositář se zdrojovými soubory i se skripty pro překlad a spuštění
1. Pohled pod kapotu JVM – přednosti a zápory využití JNI při optimalizacích (3)
V předchozí části seriálu o programovacím jazyku Java i o virtuálním stroji Javy jsme si vysvětlili, jaké problémy na vývojáře čekají ve chvíli, kdy potřebují sdílet pole (jehož prvky jsou většinou primitivního datového typu) mezi javovskou částí aplikace a částí nativní, s níž se komunikuje přes rozhraní JNI (Java Native Interface). Víme již, že většina současných virtuálních strojů Javy při předání pole do nativní funkce a při přístupu k tomuto poli provede kopii všech prvků pole, aby se zajistila práce správce paměti (garbage collector). Ten může být spuštěn zcela nezávisle na nativní funkci (která může trvat libovolně dlouho) a teprve při ukončení práce s polem je možné provést zpětnou kopii prvků a tím i synchronizaci dat. Tato technologie je zajištěna několika funkcemi JNI vypsanými v následující tabulce:
# | Návratový typ | Funkce | Popis |
---|---|---|---|
1 | jboolean * | GetBooleanArrayElements(JNIEnv *env, jbooleanArray array, jboolean *isCopy) | vrátí ukazatel na první prvek v poli typu boolean[] |
2 | jbyte * | GetByteArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) | vrátí ukazatel na první prvek v poli typu byte[] |
3 | jchar * | GetCharArrayElements(JNIEnv *env, jcharArray array, jboolean *isCopy) | vrátí ukazatel na první prvek v poli typu char[] |
4 | jshort * | GetShortArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) | vrátí ukazatel na první prvek v poli typu short[] |
5 | jint * | GetIntArrayElements(JNIEnv *env, jintArray array, jboolean *isCopy) | vrátí ukazatel na první prvek v poli typu int[] |
6 | jlong * | GetLongArrayElements(JNIEnv *env, jlongArray array, jboolean *isCopy) | vrátí ukazatel na první prvek v poli typu long[] |
7 | jfloat * | GetFloatArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) | vrátí ukazatel na první prvek v poli typu float[] |
8 | jdouble * | GetDoubleArrayElements(JNIEnv *env, jdoubleArray array, jboolean *isCopy) | vrátí ukazatel na první prvek v poli typu double[] |
Uvolnění pole a případnou zpětnou kopii zajišťují funkce:
# | Návratový typ | Funkce | Popis |
---|---|---|---|
1 | void | ReleaseBooleanArrayElements(JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode) | uvolnění pole získaného funkcí GetBooleanArrayElements |
2 | void | ReleaseByteArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) | uvolnění pole získaného funkcí GetByteArrayElements |
3 | void | ReleaseCharArrayElements(JNIEnv *env, jcharArray array, jchar *elems, jint mode) | uvolnění pole získaného funkcí GetCharArrayElements |
4 | void | ReleaseShortArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) | uvolnění pole získaného funkcí GetShortArrayElements |
5 | void | ReleaseIntArrayElements(JNIEnv *env, jintArray array, jint *elems, jint mode) | uvolnění pole získaného funkcí GetIntArrayElements |
6 | void | ReleaseLongArrayElements(JNIEnv *env, jlongArray array, jlong *elems, jint mode) | uvolnění pole získaného funkcí GetLongArrayElements |
7 | void | ReleaseFloatArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) | uvolnění pole získaného funkcí GetFloatArrayElements |
8 | void | ReleaseDoubleArrayElements(JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode) | uvolnění pole získaného funkcí GetDoubleArrayElements |
Vzhledem k tomu, že kopie celého pole může být nevýhodná jak kvůli vyšší časové složitosti, tak i paměťovým nárokům, byla do rozhraní JNI přidána další dvojice funkcí, které se (opět na většině implementací) snaží neprovádět kopii pole, protože se počítá s tím, že mezi oběma funkcemi bude pouze krátký kód, který navíc nesmí volat další funkce JNI:
# | Návratový typ | Funkce |
---|---|---|
1 | void * | GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy) |
2 | void | ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode) |
2. Benchmark měřící rychlost výpočtu průměrné hodnoty všech prvků v poli
Jakým způsobem se projeví kopie (popř. i zpětná kopie) polí v nativních funkcích si ukážeme na benchmarku. Tento benchmark provádí několikrát stejnou operaci – výpočet průměru všech prvků pole typu float[] – a to čtyřmi různými způsoby. První výpočet je založen na nativní metodě používající JNI funkce GetFloatArrayElements() a ReleaseFloatArrayElements() se zpětnou kopií pole (což je zbytečné). Druhý výpočet používá tytéž funkce, ovšem již neprovádí zpětnou kopii. Třetí nativní funkce využívá volání GetPrimitiveArrayCritical() a ReleasePrimitiveArrayCritical() a čtvrtý výpočet průměru je implementován přímo v Javě:
JNITest6.java:
/** * Jednoduchy benchmark pro porovnani rychlosti pristupu k polim * v nativnich metodach/funkcich. * * @author Pavel Tisnovsky */ public class JNITest6 { /** * Pocet opakovani zahrivaci faze benchmarku. */ private static final int WARMUP_ITERS = 15000; /** * Pocet opakovani merene faze benchmarku. */ private static final int BENCHMARK_ITERS = 15000; /** * Velikost pole, ktere bude pouzito v benchmarku. */ private static final int ARRAY_SIZE = 200000; /** * Testovaci pole. */ static float[] array = new float[ARRAY_SIZE]; /** * Naplneni pole daty. */ static { for (int i=0; i<ARRAY_SIZE; i++) { array[i] = (float)(i+1); } } /** * Nativni metody testovane benchmarkem. */ native public static float averageN1(float[] array); native public static float averageN2(float[] array); native public static float averageN3(float[] array); /** * Obdobna metoda napsana v Jave. */ public static float averageJ(float[] array) { float average = 0; // ziskat delku pole int length = array.length; // pruchod polem a vypocet sumy prvku for (int i=0; i<length; i++) { average += array[i]; } // vypocet prumeru return average/length; } /** * Spusteni benchmarku. */ private static void runJNIBenchmarks() { warmup(); benchmark(); } /** * Vypis vypocteneho vysledku (jen pro kontrolu). */ private static void printResult(float result) { System.out.print(" result="); System.out.println(result); } /** * Zahrivaci faze benchmarku. */ private static void warmup() { System.out.println("Warmup phase..."); float result; result = 0; // donutime JIT k prekladu for (int i = 0; i < WARMUP_ITERS; i++) { result = averageJ(array); } printResult(result); result = 0; // taktez zde donutime JIT k prekladu for (int i = 0; i < WARMUP_ITERS; i++) { result = averageN1(array); } printResult(result); result = 0; // taktez zde donutime JIT k prekladu for (int i = 0; i < WARMUP_ITERS; i++) { result = averageN2(array); } printResult(result); result = 0; // taktez zde donutime JIT k prekladu for (int i = 0; i < WARMUP_ITERS; i++) { result = averageN3(array); } printResult(result); System.out.println("done"); } /** * Vlastni mereny benchmark. */ private static void benchmark() { System.out.println("Benchmark phase..."); long t1, t2, delta_t; float result; // provest test a zmerit cas behu prvniho testu t1 = System.nanoTime(); result = 0; for (int i = 0; i < BENCHMARK_ITERS; i++) { result = averageJ(array); } t2 = System.nanoTime(); delta_t = t2 - t1; printResult(result); // vypis casu pro prvni test System.out.format("JITted method time: %,12d ns\n", delta_t); // provest test a zmerit cas behu druheho testu t1 = System.nanoTime(); result = 0; for (int i = 0; i < BENCHMARK_ITERS; i++) { result = averageN1(array); } t2 = System.nanoTime(); delta_t = t2 - t1; printResult(result); // vypis casu pro druhy test System.out.format("native function #1 time: %,12d ns\n", delta_t); // provest test a zmerit cas behu tretiho testu t1 = System.nanoTime(); result = 0; for (int i = 0; i < BENCHMARK_ITERS; i++) { result = averageN2(array); } t2 = System.nanoTime(); delta_t = t2 - t1; printResult(result); // vypis casu pro treti test System.out.format("native function #2 time: %,12d ns\n", delta_t); // provest test a zmerit cas behu ctvrteho testu t1 = System.nanoTime(); result = 0; for (int i = 0; i < BENCHMARK_ITERS; i++) { result = averageN3(array); } t2 = System.nanoTime(); delta_t = t2 - t1; printResult(result); // vypis casu pro ctvrty test System.out.format("native function #3 time: %,12d ns\n", delta_t); System.out.println("done"); } /** * Spusteni benchmarku. */ public static void main(String[] args) { System.loadLibrary("JNITest6"); runJNIBenchmarks(); } }
JNITest6.h (generovaný přes nástroj javah):
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class JNITest6 */ #ifndef _Included_JNITest6 #define _Included_JNITest6 #ifdef __cplusplus extern "C" { #endif #undef JNITest6_WARMUP_ITERS #define JNITest6_WARMUP_ITERS 15000L #undef JNITest6_BENCHMARK_ITERS #define JNITest6_BENCHMARK_ITERS 15000L #undef JNITest6_ARRAY_SIZE #define JNITest6_ARRAY_SIZE 100000L /* * Class: JNITest6 * Method: averageN1 * Signature: ([F)F */ JNIEXPORT jfloat JNICALL Java_JNITest6_averageN1 (JNIEnv *, jclass, jfloatArray); /* * Class: JNITest6 * Method: averageN2 * Signature: ([F)F */ JNIEXPORT jfloat JNICALL Java_JNITest6_averageN2 (JNIEnv *, jclass, jfloatArray); /* * Class: JNITest6 * Method: averageN2 * Signature: ([F)F */ JNIEXPORT jfloat JNICALL Java_JNITest6_averageN3 (JNIEnv *, jclass, jfloatArray); #ifdef __cplusplus } #endif #endif
JNITest6.c (nativní část aplikace):
#include "JNITest6.h" JNIEXPORT jfloat JNICALL Java_JNITest6_averageN1(JNIEnv *jni_env, jclass klass, jfloatArray array) { jint release_mode = 0; jfloat average = 0; /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) { average += *item; } /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleaseFloatArrayElements(jni_env, array, f, release_mode); /* vypocet prumeru */ return average/length; } JNIEXPORT jfloat JNICALL Java_JNITest6_averageN2(JNIEnv *jni_env, jclass klass, jfloatArray array) { jint release_mode = JNI_ABORT; jfloat average = 0; /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) { average += *item; } /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleaseFloatArrayElements(jni_env, array, f, release_mode); /* vypocet prumeru */ return average/length; } JNIEXPORT jfloat JNICALL Java_JNITest6_averageN3(JNIEnv *jni_env, jclass klass, jfloatArray array) { jint release_mode = JNI_ABORT; jfloat average = 0; /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (jfloat*) (*jni_env)->GetPrimitiveArrayCritical(jni_env, (jarray)array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) { average += *item; } /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleasePrimitiveArrayCritical(jni_env, (jarray)array, (void*)f, release_mode); /* vypocet prumeru */ return average/length; }
3. Překlad a spuštění benchmarku
Překlad javovské části benchmarku je velmi snadný a není rozdílný od překladu všech pěti předchozích příkladů, které jsme si v rámci popisu rozhraní JNI již vyzkoušeli:
javac JNITest6.java
Následně, tj. ve chvíli, kdy již existuje soubor JNITest6.class obsahující přeloženou třídu JNITest6, je nutné vygenerovat hlavičkový soubor JNITest6.h:
javah JNITest6
Překlad nativní části benchmarku se provede obdobným způsobem, jako tomu bylo u obou předchozích demonstračních příkladů uvedených minule (včetně voleb pro optimalizaci):
gcc -O3 -funroll-all-loops -shared \ -I/usr/lib/jvm/java-1.7.0-openjdk/include/ \ -o libJNITest6.so JNITest6.c
Popř. na platformách vyžadujících PIC (position independent code) v knihovnách:
gcc -O3 -funroll-all-loops -shared -fPIC \ -I/usr/lib/jvm/java-1.7.0-openjdk/include/ \ -I/usr/lib/jvm/java-1.7.0-openjdk/include/linux \ -o libJNITest6.so JNITest6.c
Na systému Windows s nainstalovanou OpenJDK 7 či Oracle JDK 7 by mohl překlad s využitím mingw vypadat následovně:
gcc -O3 -funroll-all-loops -shared -I"c:\Program Files\Java\jdk1.7.0_25\include" -o JNITest4.dll JNITest4.c
Povšimněte si, že při překladu byly povoleny optimalizace, aby se výsledný nativní strojový kód co nejvíce přiblížil kódu vygenerovanému JIT překladačem.
Aby bylo měření spravedlivé, je nutné při spuštění benchmarku povolit JIT překlad:
export LD_LIBRARY_PATH=. java -Xcomp JNITest6
4. Výsledky prvního benchmarku
Po spuštění benchmarku se na standardní výstup vypíšou informace o běhu i dosažené časy výpočtu. Na 64bitovém systému s procesorem Intel i7 byly získány následující hodnoty:
Warmup phase... result=100001.3 result=100001.3 result=100001.3 result=100001.3 done Benchmark phase... result=100001.3 JITted method time: 2,533,364,426 ns result=100001.3 native function #1 time: 6,630,523,834 ns result=100001.3 native function #2 time: 5,727,310,449 ns result=100001.3 native function #3 time: 2,511,554,841 ns done
Tyto výsledky jsou velmi zajímavé a možná i poněkud neočekávané. Ukazuje se totiž, že javovská varianta není (resp. v obecném případě nemusí být) nejpomalejší, ale naopak soupeří s nejrychlejší céčkovou variantou. Nejpomalejší je nativní funkce používající volání GetFloatArrayElements() a ReleaseFloatArrayElements() se zpětnou kopií, nejrychlejší je nativní funkce využívající GetPrimitiveArrayCritical() a ReleasePrimitiveArrayCritical(), z čehož je možné usuzovat, že JVM neprovedlo defenzivní kopii pole. Zde se tedy ukazuje, že se při použití nativních funkcí volaných přes rozhraní JNI vyplatí sledovat profiler a popř. se snažit zamezit zbytečné kopii polí, nebo (pokud je to možné) využít kopii jen části pole, což je problematika, kterou se budeme podrobněji zabývat v navazujících kapitolách.
5. Strojový kód generovaný JIT překladačem
Zajímavé bude porovnání strojového kódu generovaného JIT překladačem (typu server) se strojovým kódem vytvořeným céčkovým překladačem. Nejprve si uveďme, jak bude vypadat výstup JITu v případě, že byl benchmark spuštěn na 64bitovém procesoru s architekturou x86_64 s podporou rozšíření instrukčních sad MMX, SSE i SSE2. Zajímá nás samozřejmě pouze tvar metody JNITest6.averageJ(). Výstup JITu získáme následujícím způsobem:
java -server -XX:CompileThreshold=10000 -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly JNITest6
Decoding compiled method 0x009b8188: Code: [Entry Point] [Verified Entry Point] [Constants] # {method} 'averageJ' '([F)F' in 'JNITest6' 0x009b8280: int3 0x009b8281: xchg %ax,%ax 0x009b8284: mov %eax,0xffffc000(%esp) 0x009b828b: push %ebp 0x009b828c: sub $0x18,%esp 0x009b828f: mov (%ecx),%ebx 0x009b8291: mov 0xc(%ecx),%esi 0x009b8294: movss 0x8(%ecx),%xmm0 0x009b8299: movss %xmm0,0x8(%esp) 0x009b829f: mov 0x4(%ecx),%ebp 0x009b82a2: mov %ecx,(%esp) 0x009b82a5: call 0x6ee57140 ; {runtime_call} 0x009b82aa: test %esi,%esi 0x009b82ac: je 0x009b839e 0x009b82b2: mov 0x4(%esi),%ecx 0x009b82b5: cmp $0xbb30510,%ecx ; {oop({type array float})} 0x009b82bb: jne 0x009b83bd ;*iload_3 ; - JNITest6::averageJ@7 (line 41) 0x009b82c1: cmp %ebp,%ebx 0x009b82c3: jge 0x009b83d1 ;*if_icmpge ; - JNITest6::averageJ@9 (line 41) 0x009b82c9: mov 0x8(%esi),%ecx ;*faload ; - JNITest6::averageJ@15 (line 42) ; implicit exception: dispatches to 0x009b83a8 0x009b82cc: cmp %ecx,%ebx 0x009b82ce: jae 0x009b83a8 0x009b82d4: mov %ebp,%edi 0x009b82d6: dec %edi 0x009b82d7: cmp %ecx,%edi 0x009b82d9: jae 0x009b83a8 0x009b82df: mov %ebx,%edi 0x009b82e1: inc %edi ;*fload_1 ; - JNITest6::averageJ@12 (line 42) 0x009b82e2: movss 0x8(%esp),%xmm1 0x009b82e8: addss 0xc(%esi,%ebx,4),%xmm1 ;*fadd ; - JNITest6::averageJ@16 (line 42) 0x009b82ee: inc %ebx ;*iinc ; - JNITest6::averageJ@18 (line 41) 0x009b82ef: cmp %edi,%ebx 0x009b82f1: jge 0x009b82fb ;*if_icmpge ; - JNITest6::averageJ@9 (line 41) 0x009b82f3: movss %xmm1,0x8(%esp) 0x009b82f9: jmp 0x009b82e2 0x009b82fb: mov %ebp,%edi 0x009b82fd: add $0xfffffff1,%edi 0x009b8300: mov $0x80000000,%eax 0x009b8305: cmp %edi,%ebp 0x009b8307: cmovl %eax,%edi 0x009b830a: cmp %edi,%ebx 0x009b830c: jge 0x009b8377 0x009b830e: xchg %ax,%ax ;*fload_1 ; - JNITest6::averageJ@12 (line 42) 0x009b8310: addss 0xc(%esi,%ebx,4),%xmm1 0x009b8316: addss 0x10(%esi,%ebx,4),%xmm1 0x009b831c: addss 0x14(%esi,%ebx,4),%xmm1 0x009b8322: addss 0x18(%esi,%ebx,4),%xmm1 0x009b8328: addss 0x1c(%esi,%ebx,4),%xmm1 0x009b832e: addss 0x20(%esi,%ebx,4),%xmm1 0x009b8334: addss 0x24(%esi,%ebx,4),%xmm1 0x009b833a: addss 0x28(%esi,%ebx,4),%xmm1 0x009b8340: addss 0x2c(%esi,%ebx,4),%xmm1 0x009b8346: addss 0x30(%esi,%ebx,4),%xmm1 0x009b834c: addss 0x34(%esi,%ebx,4),%xmm1 0x009b8352: addss 0x38(%esi,%ebx,4),%xmm1 0x009b8358: addss 0x3c(%esi,%ebx,4),%xmm1 0x009b835e: addss 0x40(%esi,%ebx,4),%xmm1 0x009b8364: addss 0x44(%esi,%ebx,4),%xmm1 0x009b836a: addss 0x48(%esi,%ebx,4),%xmm1 ;*fadd ; - JNITest6::averageJ@16 (line 42) 0x009b8370: add $0x10,%ebx ;*iinc ; - JNITest6::averageJ@18 (line 41) 0x009b8373: cmp %edi,%ebx 0x009b8375: jl 0x009b8310 ;*if_icmpge ; - JNITest6::averageJ@9 (line 41) 0x009b8377: cmp %ebp,%ebx 0x009b8379: jge 0x009b8387 0x009b837b: nop ;*fload_1 ; - JNITest6::averageJ@12 (line 42) 0x009b837c: addss 0xc(%esi,%ebx,4),%xmm1 ;*fadd ; - JNITest6::averageJ@16 (line 42) 0x009b8382: inc %ebx ;*iinc ; - JNITest6::averageJ@18 (line 41) 0x009b8383: cmp %ebp,%ebx 0x009b8385: jl 0x009b837c ;*iload_3 ; - JNITest6::averageJ@7 (line 41) 0x009b8387: cvtsi2ss %ebp,%xmm0 0x009b838b: divss %xmm0,%xmm1 ;*fdiv ; - JNITest6::averageJ@27 (line 46) 0x009b838f: movss %xmm1,%xmm0 0x009b8393: add $0x18,%esp 0x009b8396: pop %ebp 0x009b8397: test %eax,0x940000 ; {poll_return} 0x009b839d: ret 0x009b839e: mov $0x0,%esi 0x009b83a3: jmp 0x009b82c1 0x009b83a8: mov $0xffffff86,%ecx 0x009b83ad: mov %esi,0x4(%esp) 0x009b83b1: mov %ebx,0xc(%esp) 0x009b83b5: xchg %ax,%ax 0x009b83b7: call 0x0099dd00 ; OopMap{[4]=Oop off=316} ;*fload_1 ; - JNITest6::averageJ@12 (line 42) ; {runtime_call} 0x009b83bc: int3 0x009b83bd: mov $0xffffffad,%ecx 0x009b83c2: mov %esi,0x4(%esp) 0x009b83c6: mov %ebx,0xc(%esp) 0x009b83ca: nop 0x009b83cb: call 0x0099dd00 ; OopMap{[4]=Oop off=336} ;*iload_3 ; - JNITest6::averageJ@7 (line 41) ; {runtime_call} 0x009b83d0: int3 ;*iload_3 ; - JNITest6::averageJ@7 (line 41) 0x009b83d1: movss 0x8(%esp),%xmm1 0x009b83d7: jmp 0x009b8387 0x009b83d9: hlt 0x009b83da: hlt 0x009b83db: hlt 0x009b83dc: hlt 0x009b83dd: hlt 0x009b83de: hlt 0x009b83df: hlt [Exception Handler] [Stub Code] 0x009b83e0: jmp 0x009b7240 ; {no_reloc} [Deopt Handler Code] 0x009b83e5: push $0x9b83e5 ; {section_word} 0x009b83ea: jmp 0x0099e280 ; {runtime_call} 0x009b83ef: hlt
Nejdůležitější je tělo smyčky, v níž se prochází polem a počítá se suma všech prvků tohoto pole. JIT překladač typu server se v tomto případě pokusil o rozbalení smyčky, i když poněkud naivním způsobem (hodnoty před závorkou jsou offsety):
; - JNITest6::averageJ@12 (line 42) 0x009b8310: addss 0xc(%esi,%ebx,4),%xmm1 0x009b8316: addss 0x10(%esi,%ebx,4),%xmm1 0x009b831c: addss 0x14(%esi,%ebx,4),%xmm1 0x009b8322: addss 0x18(%esi,%ebx,4),%xmm1 0x009b8328: addss 0x1c(%esi,%ebx,4),%xmm1 0x009b832e: addss 0x20(%esi,%ebx,4),%xmm1 0x009b8334: addss 0x24(%esi,%ebx,4),%xmm1 0x009b833a: addss 0x28(%esi,%ebx,4),%xmm1 0x009b8340: addss 0x2c(%esi,%ebx,4),%xmm1 0x009b8346: addss 0x30(%esi,%ebx,4),%xmm1 0x009b834c: addss 0x34(%esi,%ebx,4),%xmm1 0x009b8352: addss 0x38(%esi,%ebx,4),%xmm1 0x009b8358: addss 0x3c(%esi,%ebx,4),%xmm1 0x009b835e: addss 0x40(%esi,%ebx,4),%xmm1 0x009b8364: addss 0x44(%esi,%ebx,4),%xmm1 0x009b836a: addss 0x48(%esi,%ebx,4),%xmm1 ;*fadd ; - JNITest6::averageJ@16 (line 42) 0x009b8370: add $0x10,%ebx ;*iinc
6. Strojový kód generovaný překladačem GCC
Nyní si ukažme, jak vypadají nativní metody přeložené překladačem GCC při použití voleb -O3 a -funroll-all-loops. Překlad do assembleru lze provést dvěma způsoby: buď volbou -S (základní možnost), popř. (podrobnější výpis) použitím -Wa,–ahl (-Wa posílá následující přepínače do assembleru):
gcc -Wall -ansi -O3 -funroll-all-loops \ -g -Wa,-ahl=out.asm \ -I/usr/lib/jvm/java-1.7.0-openjdk/include/ \ -I/usr/lib/jvm/java-1.7.0-openjdk/include/linux \ -o libJNITest6.so JNITest6.c
Osobně ale preferuji jiný způsob získání čitelného kódu v assembleru. Nejprve se provede překlad společně s vygenerováním informací o symbolech používaných debuggery (-g). Následně se objektový kód zpracuje utilitou objdump s parametry -d, -S a popř. i -M intel (to pro ty z nás, kteří zrovna nemilují AT&T syntaxi :-p):
gcc -Wall -ansi -O3 -funroll-all-loops \ -g -c \ -I/usr/lib/jvm/java-1.7.0-openjdk/include/ \ -I/usr/lib/jvm/java-1.7.0-openjdk/include/linux \ JNITest6.c objdump -d -M intel -S JNITest6.o > out2.asm
Výsledek si můžete prohlédnout pod tímto odstavcem:
JNITest6.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <Java_JNITest6_averageN1>: #include "JNITest6.h" JNIEXPORT jfloat JNICALL Java_JNITest6_averageN1(JNIEnv *jni_env, jclass klass, jfloatArray array) { 0: 41 54 push r12 jint release_mode = 0; jfloat average = 0; /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); 2: 48 89 d6 mov rsi,rdx #include "JNITest6.h" JNIEXPORT jfloat JNICALL Java_JNITest6_averageN1(JNIEnv *jni_env, jclass klass, jfloatArray array) { 5: 49 89 d4 mov r12,rdx 8: 55 push rbp 9: 53 push rbx a: 48 89 fb mov rbx,rdi d: 48 83 ec 10 sub rsp,0x10 jint release_mode = 0; jfloat average = 0; /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); 11: 48 8b 07 mov rax,QWORD PTR [rdi] 14: ff 90 58 05 00 00 call QWORD PTR [rax+0x558] 1a: 89 c5 mov ebp,eax /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); 1c: 48 8b 03 mov rax,QWORD PTR [rbx] 1f: 31 d2 xor edx,edx 21: 4c 89 e6 mov rsi,r12 24: 48 89 df mov rdi,rbx 27: ff 90 e8 05 00 00 call QWORD PTR [rax+0x5e8] /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 2d: 85 ed test ebp,ebp /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); 2f: 48 89 c2 mov rdx,rax #include "JNITest6.h" JNIEXPORT jfloat JNICALL Java_JNITest6_averageN1(JNIEnv *jni_env, jclass klass, jfloatArray array) { jint release_mode = 0; jfloat average = 0; 32: 0f 57 c0 xorps xmm0,xmm0 jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 35: 0f 8e be 00 00 00 jle f9 <Java_JNITest6_averageN1+0xf9> 3b: 44 8d 4d ff lea r9d,[rbp-0x1] { average += *item; 3f: f3 0f 58 00 addss xmm0,DWORD PTR [rax] 43: b9 01 00 00 00 mov ecx,0x1 48: 41 83 e1 07 and r9d,0x7 jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 4c: 83 fd 01 cmp ebp,0x1 4f: 0f 8e a4 00 00 00 jle f9 <Java_JNITest6_averageN1+0xf9> 55: 45 85 c9 test r9d,r9d 58: 74 68 je c2 <Java_JNITest6_averageN1+0xc2> 5a: 41 83 f9 01 cmp r9d,0x1 5e: 74 55 je b5 <Java_JNITest6_averageN1+0xb5> 60: 41 83 f9 02 cmp r9d,0x2 64: 74 46 je ac <Java_JNITest6_averageN1+0xac> 66: 41 83 f9 03 cmp r9d,0x3 6a: 74 37 je a3 <Java_JNITest6_averageN1+0xa3> 6c: 41 83 f9 04 cmp r9d,0x4 70: 74 28 je 9a <Java_JNITest6_averageN1+0x9a> 72: 41 83 f9 05 cmp r9d,0x5 76: 74 19 je 91 <Java_JNITest6_averageN1+0x91> 78: 41 83 f9 06 cmp r9d,0x6 7c: 74 0a je 88 <Java_JNITest6_averageN1+0x88> /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) be: 39 cd cmp ebp,ecx c0: 7e 37 jle f9 <Java_JNITest6_averageN1+0xf9> { average += *item; c2: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] c7: f3 0f 58 44 8a 04 addss xmm0,DWORD PTR [rdx+rcx*4+0x4] cd: f3 0f 58 44 8a 08 addss xmm0,DWORD PTR [rdx+rcx*4+0x8] d3: f3 0f 58 44 8a 0c addss xmm0,DWORD PTR [rdx+rcx*4+0xc] d9: f3 0f 58 44 8a 10 addss xmm0,DWORD PTR [rdx+rcx*4+0x10] df: f3 0f 58 44 8a 14 addss xmm0,DWORD PTR [rdx+rcx*4+0x14] e5: f3 0f 58 44 8a 18 addss xmm0,DWORD PTR [rdx+rcx*4+0x18] eb: f3 0f 58 44 8a 1c addss xmm0,DWORD PTR [rdx+rcx*4+0x1c] f1: 48 83 c1 08 add rcx,0x8 jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) f5: 39 cd cmp ebp,ecx f7: 7f c9 jg c2 <Java_JNITest6_averageN1+0xc2> { average += *item; } /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleaseFloatArrayElements(jni_env, array, f, release_mode); f9: 4c 8b 03 mov r8,QWORD PTR [rbx] fc: f3 0f 11 44 24 0c movss DWORD PTR [rsp+0xc],xmm0 102: 4c 89 e6 mov rsi,r12 105: 48 89 df mov rdi,rbx 108: 31 c9 xor ecx,ecx 10a: 41 ff 90 28 06 00 00 call QWORD PTR [r8+0x628] /* vypocet prumeru */ return average/length; 111: f3 0f 2a cd cvtsi2ss xmm1,ebp 115: f3 0f 10 44 24 0c movss xmm0,DWORD PTR [rsp+0xc] } 11b: 48 83 c4 10 add rsp,0x10 11f: 5b pop rbx 120: 5d pop rbp 121: 41 5c pop r12 /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleaseFloatArrayElements(jni_env, array, f, release_mode); /* vypocet prumeru */ return average/length; 123: f3 0f 5e c1 divss xmm0,xmm1 } 127: c3 ret 128: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 12f: 00 0000000000000130 <Java_JNITest6_averageN2>: JNIEXPORT jfloat JNICALL Java_JNITest6_averageN2(JNIEnv *jni_env, jclass klass, jfloatArray array) { 130: 41 54 push r12 jint release_mode = JNI_ABORT; jfloat average = 0; /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); 132: 48 89 d6 mov rsi,rdx /* vypocet prumeru */ return average/length; } JNIEXPORT jfloat JNICALL Java_JNITest6_averageN2(JNIEnv *jni_env, jclass klass, jfloatArray array) { 135: 49 89 d4 mov r12,rdx 138: 55 push rbp 139: 53 push rbx 13a: 48 89 fb mov rbx,rdi 13d: 48 83 ec 10 sub rsp,0x10 jint release_mode = JNI_ABORT; jfloat average = 0; /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); 141: 48 8b 07 mov rax,QWORD PTR [rdi] 144: ff 90 58 05 00 00 call QWORD PTR [rax+0x558] 14a: 89 c5 mov ebp,eax /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); 14c: 48 8b 03 mov rax,QWORD PTR [rbx] 14f: 31 d2 xor edx,edx 151: 4c 89 e6 mov rsi,r12 154: 48 89 df mov rdi,rbx 157: ff 90 e8 05 00 00 call QWORD PTR [rax+0x5e8] /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 15d: 85 ed test ebp,ebp /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); 15f: 48 89 c2 mov rdx,rax } JNIEXPORT jfloat JNICALL Java_JNITest6_averageN2(JNIEnv *jni_env, jclass klass, jfloatArray array) { jint release_mode = JNI_ABORT; jfloat average = 0; 162: 0f 57 c0 xorps xmm0,xmm0 jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 165: 0f 8e be 00 00 00 jle 229 <Java_JNITest6_averageN2+0xf9> 16b: 44 8d 4d ff lea r9d,[rbp-0x1] { average += *item; 16f: f3 0f 58 00 addss xmm0,DWORD PTR [rax] 173: b9 01 00 00 00 mov ecx,0x1 178: 41 83 e1 07 and r9d,0x7 jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 17c: 83 fd 01 cmp ebp,0x1 17f: 0f 8e a4 00 00 00 jle 229 <Java_JNITest6_averageN2+0xf9> 185: 45 85 c9 test r9d,r9d 188: 74 68 je 1f2 <Java_JNITest6_averageN2+0xc2> 18a: 41 83 f9 01 cmp r9d,0x1 18e: 74 55 je 1e5 <Java_JNITest6_averageN2+0xb5> 190: 41 83 f9 02 cmp r9d,0x2 194: 74 46 je 1dc <Java_JNITest6_averageN2+0xac> 196: 41 83 f9 03 cmp r9d,0x3 19a: 74 37 je 1d3 <Java_JNITest6_averageN2+0xa3> 19c: 41 83 f9 04 cmp r9d,0x4 1a0: 74 28 je 1ca <Java_JNITest6_averageN2+0x9a> 1a2: 41 83 f9 05 cmp r9d,0x5 1a6: 74 19 je 1c1 <Java_JNITest6_averageN2+0x91> 1a8: 41 83 f9 06 cmp r9d,0x6 1ac: 74 0a je 1b8 <Java_JNITest6_averageN2+0x88> { average += *item; 1ae: f3 0f 58 40 04 addss xmm0,DWORD PTR [rax+0x4] 1b3: b9 02 00 00 00 mov ecx,0x2 1b8: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 1bd: 48 83 c1 01 add rcx,0x1 1c1: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 1c6: 48 83 c1 01 add rcx,0x1 1ca: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 1cf: 48 83 c1 01 add rcx,0x1 1d3: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 1d8: 48 83 c1 01 add rcx,0x1 1dc: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 1e1: 48 83 c1 01 add rcx,0x1 1e5: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 1ea: 48 83 c1 01 add rcx,0x1 jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 1ee: 39 cd cmp ebp,ecx 1f0: 7e 37 jle 229 <Java_JNITest6_averageN2+0xf9> { average += *item; 1f2: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 1f7: f3 0f 58 44 8a 04 addss xmm0,DWORD PTR [rdx+rcx*4+0x4] 1fd: f3 0f 58 44 8a 08 addss xmm0,DWORD PTR [rdx+rcx*4+0x8] 203: f3 0f 58 44 8a 0c addss xmm0,DWORD PTR [rdx+rcx*4+0xc] 209: f3 0f 58 44 8a 10 addss xmm0,DWORD PTR [rdx+rcx*4+0x10] 20f: f3 0f 58 44 8a 14 addss xmm0,DWORD PTR [rdx+rcx*4+0x14] 215: f3 0f 58 44 8a 18 addss xmm0,DWORD PTR [rdx+rcx*4+0x18] 21b: f3 0f 58 44 8a 1c addss xmm0,DWORD PTR [rdx+rcx*4+0x1c] 221: 48 83 c1 08 add rcx,0x8 jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 225: 39 cd cmp ebp,ecx 227: 7f c9 jg 1f2 <Java_JNITest6_averageN2+0xc2> { average += *item; } /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleaseFloatArrayElements(jni_env, array, f, release_mode); 229: 4c 8b 03 mov r8,QWORD PTR [rbx] 22c: f3 0f 11 44 24 0c movss DWORD PTR [rsp+0xc],xmm0 232: 4c 89 e6 mov rsi,r12 235: 48 89 df mov rdi,rbx 238: b9 02 00 00 00 mov ecx,0x2 23d: 41 ff 90 28 06 00 00 call QWORD PTR [r8+0x628] /* vypocet prumeru */ return average/length; 244: f3 0f 2a cd cvtsi2ss xmm1,ebp 248: f3 0f 10 44 24 0c movss xmm0,DWORD PTR [rsp+0xc] } 24e: 48 83 c4 10 add rsp,0x10 252: 5b pop rbx 253: 5d pop rbp 254: 41 5c pop r12 /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleaseFloatArrayElements(jni_env, array, f, release_mode); /* vypocet prumeru */ return average/length; 256: f3 0f 5e c1 divss xmm0,xmm1 } 25a: c3 ret 25b: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0] 0000000000000260 <Java_JNITest6_averageN3>: JNIEXPORT jfloat JNICALL Java_JNITest6_averageN3(JNIEnv *jni_env, jclass klass, jfloatArray array) { 260: 41 54 push r12 jint release_mode = JNI_ABORT; jfloat average = 0; /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); 262: 48 89 d6 mov rsi,rdx /* vypocet prumeru */ return average/length; } JNIEXPORT jfloat JNICALL Java_JNITest6_averageN3(JNIEnv *jni_env, jclass klass, jfloatArray array) { 265: 49 89 d4 mov r12,rdx 268: 55 push rbp 269: 53 push rbx 26a: 48 89 fb mov rbx,rdi 26d: 48 83 ec 10 sub rsp,0x10 jint release_mode = JNI_ABORT; jfloat average = 0; /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); 271: 48 8b 07 mov rax,QWORD PTR [rdi] 274: ff 90 58 05 00 00 call QWORD PTR [rax+0x558] 27a: 89 c5 mov ebp,eax /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (jfloat*) (*jni_env)->GetPrimitiveArrayCritical(jni_env, (jarray)array, NULL); 27c: 48 8b 03 mov rax,QWORD PTR [rbx] 27f: 31 d2 xor edx,edx 281: 4c 89 e6 mov rsi,r12 284: 48 89 df mov rdi,rbx 287: ff 90 f0 06 00 00 call QWORD PTR [rax+0x6f0] /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 28d: 85 ed test ebp,ebp /* ziskat delku pole */ jint length = (*jni_env)->GetArrayLength(jni_env, (jarray)array); /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (jfloat*) (*jni_env)->GetPrimitiveArrayCritical(jni_env, (jarray)array, NULL); 28f: 48 89 c2 mov rdx,rax } JNIEXPORT jfloat JNICALL Java_JNITest6_averageN3(JNIEnv *jni_env, jclass klass, jfloatArray array) { jint release_mode = JNI_ABORT; jfloat average = 0; 292: 0f 57 c0 xorps xmm0,xmm0 jfloat *f = (jfloat*) (*jni_env)->GetPrimitiveArrayCritical(jni_env, (jarray)array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 295: 0f 8e be 00 00 00 jle 359 <Java_JNITest6_averageN3+0xf9> 29b: 44 8d 4d ff lea r9d,[rbp-0x1] { average += *item; 29f: f3 0f 58 00 addss xmm0,DWORD PTR [rax] 2a3: b9 01 00 00 00 mov ecx,0x1 2a8: 41 83 e1 07 and r9d,0x7 jfloat *f = (jfloat*) (*jni_env)->GetPrimitiveArrayCritical(jni_env, (jarray)array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 2ac: 83 fd 01 cmp ebp,0x1 2af: 0f 8e a4 00 00 00 jle 359 <Java_JNITest6_averageN3+0xf9> 2b5: 45 85 c9 test r9d,r9d 2b8: 74 68 je 322 <Java_JNITest6_averageN3+0xc2> 2ba: 41 83 f9 01 cmp r9d,0x1 2be: 74 55 je 315 <Java_JNITest6_averageN3+0xb5> 2c0: 41 83 f9 02 cmp r9d,0x2 2c4: 74 46 je 30c <Java_JNITest6_averageN3+0xac> 2c6: 41 83 f9 03 cmp r9d,0x3 2ca: 74 37 je 303 <Java_JNITest6_averageN3+0xa3> 2cc: 41 83 f9 04 cmp r9d,0x4 2d0: 74 28 je 2fa <Java_JNITest6_averageN3+0x9a> 2d2: 41 83 f9 05 cmp r9d,0x5 2d6: 74 19 je 2f1 <Java_JNITest6_averageN3+0x91> 2d8: 41 83 f9 06 cmp r9d,0x6 2dc: 74 0a je 2e8 <Java_JNITest6_averageN3+0x88> { average += *item; 2de: f3 0f 58 40 04 addss xmm0,DWORD PTR [rax+0x4] 2e3: b9 02 00 00 00 mov ecx,0x2 2e8: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 2ed: 48 83 c1 01 add rcx,0x1 2f1: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 2f6: 48 83 c1 01 add rcx,0x1 2fa: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 2ff: 48 83 c1 01 add rcx,0x1 303: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 308: 48 83 c1 01 add rcx,0x1 30c: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 311: 48 83 c1 01 add rcx,0x1 315: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 31a: 48 83 c1 01 add rcx,0x1 jfloat *f = (jfloat*) (*jni_env)->GetPrimitiveArrayCritical(jni_env, (jarray)array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 31e: 39 cd cmp ebp,ecx 320: 7e 37 jle 359 <Java_JNITest6_averageN3+0xf9> { average += *item; 322: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 327: f3 0f 58 44 8a 04 addss xmm0,DWORD PTR [rdx+rcx*4+0x4] 32d: f3 0f 58 44 8a 08 addss xmm0,DWORD PTR [rdx+rcx*4+0x8] 333: f3 0f 58 44 8a 0c addss xmm0,DWORD PTR [rdx+rcx*4+0xc] 339: f3 0f 58 44 8a 10 addss xmm0,DWORD PTR [rdx+rcx*4+0x10] 33f: f3 0f 58 44 8a 14 addss xmm0,DWORD PTR [rdx+rcx*4+0x14] 345: f3 0f 58 44 8a 18 addss xmm0,DWORD PTR [rdx+rcx*4+0x18] 34b: f3 0f 58 44 8a 1c addss xmm0,DWORD PTR [rdx+rcx*4+0x1c] 351: 48 83 c1 08 add rcx,0x8 jfloat *f = (jfloat*) (*jni_env)->GetPrimitiveArrayCritical(jni_env, (jarray)array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f; int i; for (i=0; i<length; i++, item++) 355: 39 cd cmp ebp,ecx 357: 7f c9 jg 322 <Java_JNITest6_averageN3+0xc2> { average += *item; } /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleasePrimitiveArrayCritical(jni_env, (jarray)array, (void*)f, release_mode); 359: 4c 8b 03 mov r8,QWORD PTR [rbx] 35c: f3 0f 11 44 24 0c movss DWORD PTR [rsp+0xc],xmm0 362: 4c 89 e6 mov rsi,r12 365: 48 89 df mov rdi,rbx 368: b9 02 00 00 00 mov ecx,0x2 36d: 41 ff 90 f8 06 00 00 call QWORD PTR [r8+0x6f8] /* vypocet prumeru */ return average/length; 374: f3 0f 2a cd cvtsi2ss xmm1,ebp 378: f3 0f 10 44 24 0c movss xmm0,DWORD PTR [rsp+0xc] } 37e: 48 83 c4 10 add rsp,0x10 382: 5b pop rbx 383: 5d pop rbp 384: 41 5c pop r12 /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleasePrimitiveArrayCritical(jni_env, (jarray)array, (void*)f, release_mode); /* vypocet prumeru */ return average/length; 386: f3 0f 5e c1 divss xmm0,xmm1 } 38a: c3 ret
Opět je zajímavé se podívat na to, jak se překladači (který nyní neměl žádné informace o délce pole, na rozdíl od JITu!) podařilo rozbalit kritickou část kódu, tj. programovou smyčku pro výpočet sumy všech prvků pole. Kvůli rozbalení smyčky je poněkud složitější test na ukončení průchodu:
2ac: 83 fd 01 cmp ebp,0x1 2af: 0f 8e a4 00 00 00 jle 359 <Java_JNITest6_averageN3+0xf9> 2b5: 45 85 c9 test r9d,r9d 2b8: 74 68 je 322 <Java_JNITest6_averageN3+0xc2> 2ba: 41 83 f9 01 cmp r9d,0x1 2be: 74 55 je 315 <Java_JNITest6_averageN3+0xb5> 2c0: 41 83 f9 02 cmp r9d,0x2 2c4: 74 46 je 30c <Java_JNITest6_averageN3+0xac> 2c6: 41 83 f9 03 cmp r9d,0x3 2ca: 74 37 je 303 <Java_JNITest6_averageN3+0xa3> 2cc: 41 83 f9 04 cmp r9d,0x4 2d0: 74 28 je 2fa <Java_JNITest6_averageN3+0x9a> 2d2: 41 83 f9 05 cmp r9d,0x5 2d6: 74 19 je 2f1 <Java_JNITest6_averageN3+0x91> 2d8: 41 83 f9 06 cmp r9d,0x6 2dc: 74 0a je 2e8 <Java_JNITest6_averageN3+0x88>
…což je kombinováno s kódem pro přičtení posledních 0 až 7 prvků (povšimněte si cílů skoků v předchozím bloku instrukcí cmp (compare) a je (jump if equal):
2de: f3 0f 58 40 04 addss xmm0,DWORD PTR [rax+0x4] 2e3: b9 02 00 00 00 mov ecx,0x2 2e8: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 2ed: 48 83 c1 01 add rcx,0x1 2f1: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 2f6: 48 83 c1 01 add rcx,0x1 2fa: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 2ff: 48 83 c1 01 add rcx,0x1 303: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 308: 48 83 c1 01 add rcx,0x1 30c: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 311: 48 83 c1 01 add rcx,0x1 315: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 31a: 48 83 c1 01 add rcx,0x1
Vlastní smyčka je potom osmkrát rozbalena, takže vypadá prakticky stejně jako výsledek JITu (ten však rozbalil smyčku 16×, což už je ale zbytečně mnoho):
322: f3 0f 58 04 8a addss xmm0,DWORD PTR [rdx+rcx*4] 327: f3 0f 58 44 8a 04 addss xmm0,DWORD PTR [rdx+rcx*4+0x4] 32d: f3 0f 58 44 8a 08 addss xmm0,DWORD PTR [rdx+rcx*4+0x8] 333: f3 0f 58 44 8a 0c addss xmm0,DWORD PTR [rdx+rcx*4+0xc] 339: f3 0f 58 44 8a 10 addss xmm0,DWORD PTR [rdx+rcx*4+0x10] 33f: f3 0f 58 44 8a 14 addss xmm0,DWORD PTR [rdx+rcx*4+0x14] 345: f3 0f 58 44 8a 18 addss xmm0,DWORD PTR [rdx+rcx*4+0x18] 34b: f3 0f 58 44 8a 1c addss xmm0,DWORD PTR [rdx+rcx*4+0x1c] 351: 48 83 c1 08 add rcx,0x8
7. Částečné řešení problému při sdílení polí – využití regionů
I přes optimalizace provedené GCC je však zřejmé, že kopie celých polí není a ani nemůže být optimálním řešením. Navíc platí, že u JNI funkcí GetPrimitiveArrayCritical() a ReleasePrimitiveArrayCritical() není zaručeno, že se kopie neprovede. Ovšem v případě, že nativní část aplikace nemusí přistupovat k celému poli, ale pouze k jeho části, je možné provést kopii pouze této vybrané části, a to s využitím následujících JNI funkcí:
# | Návratový typ | Funkce | Popis |
---|---|---|---|
1 | void | GetBooleanArrayRegion(JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf) | kopie prvků typu jboolean |
2 | void | GetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf) | kopie prvků typu jbyte |
3 | void | GetCharArrayRegion(JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf) | kopie prvků typu jchar |
4 | void | GetShortArrayRegion(JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf) | kopie prvků typu jshort |
5 | void | GetIntArrayRegion(JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf) | kopie prvků typu jint |
6 | void | GetLongArrayRegion(JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf) | kopie prvků typu jlong |
7 | void | GetFloatArrayRegion(JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf) | kopie prvků typu jfloat |
8 | void | GetDoubleArrayRegion(JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf) | kopie prvků typu jdouble |
Největším rozdílem oproti všem výše popsaným funkcím je fakt, že se programátor musí sám postarat (funkcí malloc() apod.) o alokaci paměti dostatečně velkou na to, aby pojala len prvků daného typu a samozřejmě je nutné tento paměťový region opět uvolnit funkcí free().
Pokud je nutné prvky zapsat zpět do pole sdíleného s javovskou částí aplikace, použijí se následující funkce:
# | Návratový typ | Funkce | Popis |
---|---|---|---|
1 | void | SetBooleanArrayRegion(JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf) | kopie prvků typu jboolean |
2 | void | SetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf) | kopie prvků typu jbyte |
3 | void | SetCharArrayRegion(JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf) | kopie prvků typu jchar |
4 | void | SetShortArrayRegion(JNIEnv *env, jshortArray array, jsize start, jsize len, const jbyte jshort *buf) | kopie prvků typu jshort |
5 | void | SetIntArrayRegion(JNIEnv *env, jintArray array, jsize start, jsize len, const jbyte jint *buf) | kopie prvků typu jint |
6 | void | SetLongArrayRegion(JNIEnv *env, jlongArray array, jsize start, jsize len, const jbyte jlong *buf) | kopie prvků typu jlong |
7 | void | SetFloatArrayRegion(JNIEnv *env, jfloatArray array, jsize start, jsize len, const jbyte jfloat *buf) | kopie prvků typu jfloat |
8 | void | SetDoubleArrayRegion(JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jbyte jdouble *buf) | kopie prvků typu jdouble |
8. Benchmark pracující pouze s částí sdíleného pole
Nyní si již můžeme na dalším benchmarku vyzkoušet, co se stane, pokud budeme chtít vypočítat sumu jen z těch prvků, které tvoří pouze část pole. Tato situace nastává poměrně často, například při zpracování obrazu či jiného signálu. Dnešní druhý benchmark se v mnoha ohledech podobá benchmarku prvnímu, větší změny však nastanou v nativní části aplikace:
/** * Jednoduchy benchmark pro porovnani rychlosti pristupu k vybranym * prvkum poli v nativnich metodach/funkcich. * * @author Pavel Tisnovsky */ public class JNITest7 { /** * Pocet opakovani zahrivaci faze benchmarku. */ private static final int WARMUP_ITERS = 15000; /** * Pocet opakovani merene faze benchmarku. */ private static final int BENCHMARK_ITERS = 15000; /** * Velikost pole, ktere bude pouzito v benchmarku. */ private static final int ARRAY_SIZE = 200000; /** * Prvni zpracovavany prvek pole. */ private static final int FIRST_ARRAY_ITEM = ARRAY_SIZE / 2; /** * Posledni zpracovavany prvek pole. */ private static final int LAST_ARRAY_ITEM = 2 * ARRAY_SIZE / 3; //private static final int LAST_ARRAY_ITEM = FIRST_ARRAY_ITEM + 100; /** * Testovaci pole. */ static float[] array = new float[ARRAY_SIZE]; /** * Naplneni pole daty. */ static { for (int i=0; i < ARRAY_SIZE; i++) { array[i] = (float)(i+1); } } /** * Nativni metody testovane benchmarkem. */ native public static float sumN1(float[] array, int first_item, int last_item); native public static float sumN2(float[] array, int first_item, int last_item); native public static float sumN3(float[] array, int first_item, int last_item); native public static float sumN4(float[] array, int first_item, int last_item); /** * Obdobna metoda napsana v Jave. */ public static float sumJ(float[] array, int first_item, int last_item) { float sum = 0; // pruchod polem a vypocet sumy prvku for (int i=first_item; i < last_item; i++) { sum += array[i]; } // vraceni vysledku return sum; } /** * Spusteni benchmarku. */ private static void runJNIBenchmarks() { warmup(); benchmark(); } /** * Vypis vypocteneho vysledku (jen pro kontrolu). */ private static void printResult(float result) { System.out.print(" result="); System.out.println(result); } /** * Zahrivaci faze benchmarku. */ private static void warmup() { System.out.println("Warmup phase..."); float result; result = 0; // donutime JIT k prekladu for (int i = 0; i < WARMUP_ITERS; i++) { result = sumJ(array, FIRST_ARRAY_ITEM, LAST_ARRAY_ITEM); } printResult(result); result = 0; // taktez zde donutime JIT k prekladu for (int i = 0; i < WARMUP_ITERS; i++) { result = sumN1(array, FIRST_ARRAY_ITEM, LAST_ARRAY_ITEM); } printResult(result); result = 0; // taktez zde donutime JIT k prekladu for (int i = 0; i < WARMUP_ITERS; i++) { result = sumN2(array, FIRST_ARRAY_ITEM, LAST_ARRAY_ITEM); } printResult(result); result = 0; // taktez zde donutime JIT k prekladu for (int i = 0; i < WARMUP_ITERS; i++) { result = sumN3(array, FIRST_ARRAY_ITEM, LAST_ARRAY_ITEM); } printResult(result); result = 0; // taktez zde donutime JIT k prekladu for (int i = 0; i < WARMUP_ITERS; i++) { result = sumN4(array, FIRST_ARRAY_ITEM, LAST_ARRAY_ITEM); } printResult(result); System.out.println("done"); } /** * Vlastni mereny benchmark. */ private static void benchmark() { System.out.println("Benchmark phase..."); long t1, t2, delta_t; float result; // provest test a zmerit cas behu prvniho testu t1 = System.nanoTime(); result = 0; for (int i = 0; i < BENCHMARK_ITERS; i++) { result = sumJ(array, FIRST_ARRAY_ITEM, LAST_ARRAY_ITEM); } t2 = System.nanoTime(); delta_t = t2 - t1; printResult(result); // vypis casu pro prvni test System.out.format("JITted method time: %,12d ns\n", delta_t); // provest test a zmerit cas behu druheho testu t1 = System.nanoTime(); result = 0; for (int i = 0; i < BENCHMARK_ITERS; i++) { result = sumN1(array, FIRST_ARRAY_ITEM, LAST_ARRAY_ITEM); } t2 = System.nanoTime(); delta_t = t2 - t1; printResult(result); // vypis casu pro druhy test System.out.format("native function #1 time: %,12d ns\n", delta_t); // provest test a zmerit cas behu tretiho testu t1 = System.nanoTime(); result = 0; for (int i = 0; i < BENCHMARK_ITERS; i++) { result = sumN2(array, FIRST_ARRAY_ITEM, LAST_ARRAY_ITEM); } t2 = System.nanoTime(); delta_t = t2 - t1; printResult(result); // vypis casu pro treti test System.out.format("native function #2 time: %,12d ns\n", delta_t); // provest test a zmerit cas behu ctvrteho testu t1 = System.nanoTime(); result = 0; for (int i = 0; i < BENCHMARK_ITERS; i++) { result = sumN3(array, FIRST_ARRAY_ITEM, LAST_ARRAY_ITEM); } t2 = System.nanoTime(); delta_t = t2 - t1; printResult(result); // vypis casu pro ctvrty test System.out.format("native function #3 time: %,12d ns\n", delta_t); // provest test a zmerit cas behu pateho testu t1 = System.nanoTime(); result = 0; for (int i = 0; i < BENCHMARK_ITERS; i++) { result = sumN4(array, FIRST_ARRAY_ITEM, LAST_ARRAY_ITEM); } t2 = System.nanoTime(); delta_t = t2 - t1; printResult(result); // vypis casu pro paty test System.out.format("native function #4 time: %,12d ns\n", delta_t); System.out.println("done"); } /** * Spusteni benchmarku. */ public static void main(String[] args) { System.loadLibrary("JNITest7"); runJNIBenchmarks(); } }
Hlavičkový soubor pro nativní metody:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class JNITest7 */ #ifndef _Included_JNITest7 #define _Included_JNITest7 #ifdef __cplusplus extern "C" { #endif #undef JNITest7_WARMUP_ITERS #define JNITest7_WARMUP_ITERS 15000L #undef JNITest7_BENCHMARK_ITERS #define JNITest7_BENCHMARK_ITERS 15000L #undef JNITest7_ARRAY_SIZE #define JNITest7_ARRAY_SIZE 200000L #undef JNITest7_FIRST_ARRAY_ITEM #define JNITest7_FIRST_ARRAY_ITEM 100000L #undef JNITest7_LAST_ARRAY_ITEM #define JNITest7_LAST_ARRAY_ITEM 200000L /* * Class: JNITest7 * Method: sumN1 * Signature: ([FII)F */ JNIEXPORT jfloat JNICALL Java_JNITest7_sumN1 (JNIEnv *, jclass, jfloatArray, jint, jint); /* * Class: JNITest7 * Method: sumN2 * Signature: ([FII)F */ JNIEXPORT jfloat JNICALL Java_JNITest7_sumN2 (JNIEnv *, jclass, jfloatArray, jint, jint); /* * Class: JNITest7 * Method: sumN3 * Signature: ([FII)F */ JNIEXPORT jfloat JNICALL Java_JNITest7_sumN3 (JNIEnv *, jclass, jfloatArray, jint, jint); /* * Class: JNITest7 * Method: sumN4 * Signature: ([FII)F */ JNIEXPORT jfloat JNICALL Java_JNITest7_sumN4 (JNIEnv *, jclass, jfloatArray, jint, jint); #ifdef __cplusplus } #endif #endif
Nativní metody měřené benchmarkem jsou čtyři a mají následující vlastnosti:
Metoda | Popis |
---|---|
sumN1() | používá GetFloatArrayElements() a ReleaseFloatArrayElements() se zpětnou kopií prvků |
sumN2() | používá GetFloatArrayElements() a ReleaseFloatArrayElements() bez zpětné kopie prvků |
sumN3() | používá GetPrimitiveArrayCritical() a ReleasePrimitiveArrayCritical() |
sumN4() | používá GetFloatArrayRegion() s ruční alokací a dealokací paměti |
Podívejme se tedy na celý kód nativní části aplikace:
#include <stdlib.h> #include "JNITest7.h" /* * Class: JNITest7 * Method: sumN1 * Signature: ([FII)F */ JNIEXPORT jfloat JNICALL Java_JNITest7_sumN1( JNIEnv *jni_env, jclass klass, jfloatArray array, jint first_item, jint last_item) { jint release_mode = 0; jfloat sum = 0; /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f+first_item; int i; for (i=first_item; i<last_item; i++, item++) { sum += *item; } /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleaseFloatArrayElements(jni_env, array, f, release_mode); /* vraceni vypoctene sumy */ return sum; } /* * Class: JNITest7 * Method: sumN2 * Signature: ([FII)F */ JNIEXPORT jfloat JNICALL Java_JNITest7_sumN2( JNIEnv *jni_env, jclass klass, jfloatArray array, jint first_item, jint last_item) { jint release_mode = JNI_ABORT; jfloat sum = 0; /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (*jni_env)->GetFloatArrayElements(jni_env, array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f+first_item; int i; for (i=first_item; i<last_item; i++, item++) { sum += *item; } /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleaseFloatArrayElements(jni_env, array, f, release_mode); /* vraceni vypoctene sumy */ return sum; } /* * Class: JNITest7 * Method: sumN3 * Signature: ([FII)F */ JNIEXPORT jfloat JNICALL Java_JNITest7_sumN3( JNIEnv *jni_env, jclass klass, jfloatArray array, jint first_item, jint last_item) { jint release_mode = JNI_ABORT; jfloat sum = 0; /* prevod na "ceckove" pole prvku typu float */ jfloat *f = (jfloat*) (*jni_env)->GetPrimitiveArrayCritical(jni_env, (jarray)array, NULL); /* pruchod polem a vypocet sumy prvku */ jfloat *item = f+first_item; int i; for (i=first_item; i<last_item; i++, item++) { sum += *item; } /* uvolnit ceckove pole s pripadnou zpetnou kopii prvku */ (*jni_env)->ReleasePrimitiveArrayCritical(jni_env, (jarray)array, (void*)f, release_mode); /* vraceni vypoctene sumy */ return sum; } /* * Class: JNITest7 * Method: sumN4 * Signature: ([FII)F */ JNIEXPORT jfloat JNICALL Java_JNITest7_sumN4( JNIEnv *jni_env, jclass klass, jfloatArray array, jint first_item, jint last_item) { jfloat sum = 0; /* alokace pole */ jfloat *c_array = (jfloat*)malloc((sizeof(jfloat)) * (last_item-first_item)); /* kopie pole */ (*jni_env)->GetFloatArrayRegion(jni_env, array, first_item, last_item-first_item, c_array); /* pruchod polem a vypocet sumy prvku */ jfloat *item = c_array; int i; for (i=first_item; i<last_item; i++, item++) { sum += *item; } /* alokovane pole je zapotrebi uvolnit */ free(c_array); /* vraceni vypoctene sumy */ return sum; }
9. Překlad a spuštění benchmarku
V rychlosti si řekněme jak probíhá překlad.
Javovská část aplikace:
javac JNITest7.java
Vygenerování hlavičkového souboru:
javah JNITest7
Překlad nativní části aplikace (jeden ze způsobů):
gcc -O3 -funroll-all-loops -shared \ -I/usr/lib/jvm/java-1.7.0-openjdk/include/ \ -o libJNITest7.so JNITest7.c gcc -O3 -funroll-all-loops -shared -fPIC \ -I/usr/lib/jvm/java-1.7.0-openjdk/include/ \ -I/usr/lib/jvm/java-1.7.0-openjdk/include/linux \ -o libJNITest7.so JNITest7.c gcc -O3 -funroll-all-loops -shared -I"c:\Program Files\Java\jdk1.7.0_25\include" -o JNITest4.dll JNITest4.c
10. Výsledky benchmarku
Výsledky benchmarku si uvedeme pro dva případy. V prvním případě se suma počítala pro poměrně velkou část pole, kde první a poslední prvek byly určeny konstantami:
private static final int FIRST_ARRAY_ITEM = ARRAY_SIZE / 2; private static final int LAST_ARRAY_ITEM = 2 * ARRAY_SIZE / 3;
Warmup phase... result=1.49999555E10 result=1.49999555E10 result=1.49999555E10 result=1.49999555E10 result=1.49999555E10 done Benchmark phase... result=1.49999555E10 JITted method time: 1,255,174,174 ns result=1.49999555E10 native function #1 time: 5,426,085,733 ns result=1.49999555E10 native function #2 time: 4,423,321,655 ns result=1.49999555E10 native function #3 time: 1,256,383,554 ns result=1.49999555E10 native function #4 time: 2,448,692,621 ns done
Již zde je patrné, jak je kopie celých polí ve funkcích sumN1() a sumN2(), poměrně neefektivní je i kopie části pole ve funkci sumN4.
Pro porovnání se podívejme na to, co se stane v případě, že se suma bude počítat pro pouhých sto prvků pole:
private static final int FIRST_ARRAY_ITEM = ARRAY_SIZE / 2; private static final int LAST_ARRAY_ITEM = FIRST_ARRAY_ITEM + 100;
Zde již samozřejmě budou časy mnohem odlišnější, a to u těch metod a funkcí, které nemusí kopírovat celá pole:
Warmup phase... result=1.000505E7 result=1.000505E7 result=1.000505E7 result=1.000505E7 result=1.000505E7 done Benchmark phase... result=1.000505E7 JITted method time: 922,809 ns result=1.000505E7 native function #1 time: 4,163,715,458 ns result=1.000505E7 native function #2 time: 3,151,356,483 ns result=1.000505E7 native function #3 time: 1,676,210 ns result=1.000505E7 native function #4 time: 2,357,485 ns done
Ponaučení: rozhraní JNI sice může v některých případech pomoci při optimalizaci aplikací, ale nelze se v žádném případě spoléhat na to, že jeho použití je samospasitelné. Problematickým místem je například přenos polí mezi javovskou a nativní částí aplikace, kdy (mnohdy vynucená) kopie prvků polí zcela znehodnotí jakékoli výhody, které by z použití nativního kódu mohly vyplynout.
Ponaučení2: používat mikrobencharky a profiler jako základní nástroje ještě před prováděním jakýchkoli optimalizací.
11. Repositář se zdrojovými soubory i se skripty pro překlad a spuštění
Následuje – v tomto seriálu již tradiční – kapitola s odkazy na zdrojové kódy uložené do Mercurial repositáře. V následující tabulce najdete odkazy na prozatím nejnovější verzi dnes použitých demonstračních příkladů (benchmarků):
12. Odkazy na Internetu
- Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - MultiMedia eXtensions
http://softpixel.com/~cwright/programming/simd/mmx.phpi - SSE (Streaming SIMD Extentions)
http://www.songho.ca/misc/sse/sse.html - Timothy A. Chagnon: SSE and SSE2
http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf - Intel corporation: Extending the Worldr's Most Popular Processor Architecture
http://download.intel.com/technology/architecture/new-instructions-paper.pdf - SIMD architectures:
http://arstechnica.com/old/content/2000/03/simd.ars/ - GC safe-point (or safepoint) and safe-region
http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html - Safepoints in HotSpot JVM
http://blog.ragozin.info/2012/10/safepoints-in-hotspot-jvm.html - Java theory and practice: Synchronization optimizations in Mustang
http://www.ibm.com/developerworks/java/library/j-jtp10185/ - How to build hsdis
http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/tip/src/share/tools/hsdis/README - Java SE 6 Performance White Paper
http://www.oracle.com/technetwork/java/6-performance-137236.html - Lukas Stadler's Blog
http://classparser.blogspot.cz/2010/03/hsdis-i386dll.html - How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
http://dropzone.nfshost.com/hsdis.htm - PrintAssembly
https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly - The Java Virtual Machine Specification: 3.14. Synchronization
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.14 - The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4 - The Java Virtual Machine Specification: 17.4. Memory Model
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 - The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 - Open Source ByteCode Libraries in Java
http://java-source.net/open-source/bytecode-libraries - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - BCEL Home page
http://commons.apache.org/bcel/ - Byte Code Engineering Library (před verzí 5.0)
http://bcel.sourceforge.net/ - Byte Code Engineering Library (verze >= 5.0)
http://commons.apache.org/proper/commons-bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - Javassist
http://www.jboss.org/javassist/ - Byteman
http://www.jboss.org/byteman - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - jclasslib bytecode viewer
http://www.ej-technologies.com/products/jclasslib/overview.html