2012-07-24 3 views
8

Ich weiß, Java ist eine sichere Sprache, aber wenn Matrixberechnungen benötigt werden, kann ich etwas schneller versuchen?Code Injizieren/Assembly Inlining in Java?

Ich lerne __asm ​​{} in C++, Digital-Mars-Compiler und FASM. Ich möchte dasselbe in Java machen. Wie kann ich Inline-Assembler-Codes in Funktionen einbauen? Ist das überhaupt möglich?

Etwas Ähnliches (a vektorisierten Schleife alle Elemente eines Arrays auf einen Wert klemmen, ohne Verzweigung, unter Verwendung von AVX Unterstützung der CPU):

JavaAsmBlock(
    # get pointers into registers somehow 
    # and tell Java which registers the asm clobbers somehow 
    vbroadcastss twenty_five(%rip), %ymm0 
    xor %edx,%edx 
.Lloop:       # do { 
    vmovups (%rsi, %rdx, 4), %ymm1 
    vcmpltps %ymm1, %ymm0, %ymm2 
    vblendvps %ymm2, %ymm0, %ymm1, %ymm1 
    vmovups %ymm1, (%rdi, %rdx, 4) 
    # TODO: unroll the loop a bit, and maybe use aligned loads/stores in the main loop 
    add   $32, %rdx 
    cmp   %rcx, %rdx 
    jb  .Lloop     # } while(idx < count) 
); 

System.out.println(var[0]); 

Ich möchte nicht einen Code-Injektor verwenden. Ich möchte die Intel® oder AT & T Stil x86 Anweisungen sehen.

+0

Wenn Sie schreiben asm wie die (16-Bit-Register und mit 'div' von 4 anstelle eines' shr al, 2'), [es ist definitiv * nicht * gehen schneller zu sein als das, was ein C-Compiler könnte machen für dich.] (https://stackoverflow.com/questions/40354978/why-is-this-c-code-faster-than-my-hand-written-assembly-for-testing-the-collat/40355466# 40355466), also sollten Sie einfach JNI mit C oder C++ verwenden. ASM ist nur dann nützlich für die Performance, wenn Sie wissen, wie Sie die Mikroarchitektur aktueller CPUs einstellen können. Dies ist eine nützliche Frage, aber das Beispiel ist ein Beispiel dafür, warum die meisten Leute * asm nicht verwenden sollten. –

+0

Sie haben Recht. Zwei Dinge gleichzeitig. Ich würde etwas wie ein AVX-Punktprodukt mit der richtigen Reihenfolge der Anweisungen hinzufügen, wenn ich zu diesem Zeitpunkt genug Erfahrung hatte. –

+0

Sie könnten die Frage bearbeiten, um etwas Modernes zu verwenden. Wie vielleicht BMI2 'pdep', die kein Java intrinsisch hat. Im Idealfall könnten Sie sich etwas einfallen lassen, das Sie nicht so leicht einen C-Compiler für sich aussenden könnten. –

Antwort

13

Es gibt eine Abstraktionsschicht zwischen Ihrem Java-Code und der zugrunde liegenden Hardware, die solche Dinge prinzipiell unmöglich macht; Sie können technisch nicht wissen, wie Ihr Code auf dem zugrunde liegenden Computer dargestellt wird, da der gleiche Bytecode auf verschiedenen Prozessoren und unterschiedlichen Architekturen ausgeführt werden kann.

Was Sie offiziell können tun ist die Java Native Interface (JNI), um nativen Code von Ihrem Java-Code aufzurufen. Der Anruf-Overhead ist beträchtlich, und das Teilen von Daten mit Java ist ziemlich teuer, daher sollte dies nur für Stücke von nativem Code mit anständiger Größe verwendet werden.

Theoretisch sollte eine solche Erweiterung jedoch möglich sein. Man kann sich einen Java-Compiler vorstellen, der auf eine bestimmte Plattform abzielt und Assembly-Fluchten erlaubt. Der Compiler müsste seinen ABI veröffentlichen, damit Sie die Aufrufkonventionen kennen. Mir ist das aber nicht bewusst. Aber es gibt severalcompilersavailable, die Java direkt in nativen Code kompilieren; Es ist möglich, dass einer von ihnen so etwas ohne mein Wissen unterstützt oder erweitert werden könnte.

schließlich auf einer anderen Ebene zusammen, es Bytecode Monteuren für die JVM sind, wie Jasmin. Ein Bytecode-Assembler können Sie „Maschinencode“ schreiben, die die JVM direkt gerichtet ist, und manchmal kann man besser Code als der javac Compiler schreiben kann generieren. Es macht Spaß zu spielen, auf jeden Fall.

+0

Ok. İ wird Bytecode-Assembler auch versuchen –

+2

Von den verfügbaren Java-zu-Native-Code-Compilern vor der Zeit implementiert [Excelsior JET] (http://www.excelsiorjet.com) nur JNI, während [GCJ] (http: // gcc .gnu.org/java /) unterstützt sowohl JNI als auch seine eigene Schnittstelle namens [CNI] (http://gcc.gnu.org/onlinedocs/gcj/About-CNI.html). –

2

Sie können Assembly nicht direkt von Java aufrufen. Aber Sie können C-Code über JNI aufrufen, und von dort können Sie Assembly aufrufen.

This article shows how.

+0

sehr nett. Ich werde das versuchen. Ich verwende den digitalen Mars-Compiler. denkst du es ist möglich mit __asm? Nwm ich werde es selbst versuchen. danke –

+0

Soweit ich mich erinnern kann, können Sie, was auch immer c Compiler Sie mögen. Java verwendet einfach die Plattform abi. –

+0

Sie können eine Funktion in Assembly schreiben, die dem C ABI folgt, und können daher genauso wie eine C-Funktion aufgerufen werden. Grundsätzlich, was auch immer Sie in einer C-Funktion tun würden, um es JNI-kompatibel zu machen, können Sie in asm tun. –

1

Sie verwenden JNI oder JNA und Ihre native Funktionen von Java aufrufen. Oder als Alternative haben Sie Bytecode als InputStream und bilden eine Java-Klasse daraus.

1

Sie können auch einen Blick auf Aparapi werfen.

+0

isnt aparapi für parallele Programmierung für GPU? –

+3

Ja. Hast du nicht gefragt, wie man Matrixberechnungen schneller macht? –

2

Es ist möglich, Assembly von Java unter Verwendung der Machine Level Java Technologie aufzurufen. Es packt transparent Ihren Assembler-Code, der in Java geschrieben ist, aber sehr ähnlich der am häufigsten verwendeten Assembly-Syntax, in eine native Bibliothek. Und als nächstes müssen Sie nur eine native Methode aufrufen, die Sie in derselben Klasse definieren, in der Ihre Assembly geschrieben ist. Sie bleiben also immer in der Java-Umgebung und müssen nicht von der Java-IDE zu einigen Assemblierungswerkzeugen und dann zurück zu Java wechseln.

+0

Sieht aus wie die API, die Sie vorschlagen, fehlt Dokumentation. Können Sie mehr Details angeben? –

+0

Lower API/Interface Latenz als jni? –

5

Sie können in Ihrem Java-Code keine direkte Inline-Assemblierung durchführen. Im Gegensatz zu dem, was von einigen anderen Antworten behauptet wird, ist das bequeme Aufrufen von Assembly ohne Durchlaufen einer Zwischenschicht C (oder C++) möglich.

Lösungsweg

Betrachten Sie die folgende Java-Klasse:

public class MyJNIClass { 

    public native void printVersion(); 

} 

Die Hauptidee ist es, ein Symbol mit der JNI-Namenskonvention zu erklären. In diesem Fall lautet der in Ihrem Assembly-Code zu verwendende Name Java_MyJNIClass_printVersion. Dieses Symbol muss von anderen Übersetzungseinheiten sichtbar sein, was z. B. mit der Richtlinie public in FASM oder der Richtlinie global in NASM erreicht werden kann.

Schreiben Sie Ihren Assembler-Code mit den Aufrufkonventionen der Zielarchitektur (Argumente in Registern übergeben werden kann, auf dem Stapel, in anderen Speicherstrukturen, etc.). Das erste an Ihre Assembly-Funktion übergebene Argument ist ein Zeiger auf JNIEnv, der selbst ein Zeiger auf die JNI-Funktionstabelle ist. Verwenden Sie es, um JNI-Funktionen aufzurufen. Zum Beispiel NASM mit und Targeting x86_64:

global Java_MyJNIClass_printVersion 

section .text 

Java_MyJNIClass_printVersion: 
    mov rax, [rdi] 
    call [rax + 8*4] ; pointer size in x86_64 * index of GetVersion 
    ... 

Indizes für JNI-Funktionen können in den Java documentation finden. Da die JNI-Funktionstabelle im Grunde ein Array von Zeigern ist, vergessen Sie nicht, diese Indizes mit der Größe eines Zeigers in der Zielarchitektur zu multiplizieren.

Das zweite an Ihre Assembly-Funktion übergebene Argument ist eine Referenz auf die aufrufende Java-Klasse oder das aufrufende Objekt. Alle nachfolgenden Argumente sind die Parameter Ihrer systemeigenen Java-Methode.

Schließlich assemblieren Sie Ihren Code, um eine Objektdatei zu generieren, und erstellen Sie dann eine gemeinsam genutzte Bibliothek aus dieser Objektdatei (GCC kann diesen letzten Schritt mit einem ähnlichen Befehl wie gcc -shared -o ... ausführen).

Beispiel Laufen

Ich habe ein fully runnable example on GitHub erstellt, um einen Blick zu nehmen fühlen sich frei und mit ihm spielen, um zu einem besseren Verständnis zu bekommen.

+0

das ist also noch tiefer als JNI C++? –

+0

Nun, es verwendet die gleiche JNI-Implementierung wie mit C oder C++, aber ja, von einer niedrigeren Ebene. ;-) – Pyves

+1

Du hättest 'mov rax, [rdi]'/'call [rax + 8 * 4]' schreiben können. x86-Adressierungsmodi sind effizienter als zusätzliche Anweisungen. Speicher-indirekter Aufruf ist nicht schneller als load + call, aber er ist auch nicht langsamer und speichert die Codegröße und dekodiert die Bandbreite. (Hmm, eigentlich laut http://agner.org/optimize/, könnte es bei AMD langsamer sein, da es mehr als 2 Ups ist und das bedeutet VectorPath (mikrocodiert), nicht DirectPath. Wenn man für AMD stimmt, vielleicht 'mov rax , [rdi] '/' mov rax, [rax + 8 * 4] '/' call rax'. Noch keine ADD-Anweisung, das ist immer schlimmer) –