2012-07-24 74 views
8

我知道Java是一種安全的語言,但是當需要矩陣計算時,我可以更快地嘗試一些東西嗎?Java中的代碼注入/裝配內聯?

我正在學習C++,數字火星編譯器和FASM中的__asm {}。我想在Java中做同樣的事情。如何在函數中內聯彙編代碼?這甚至有可能嗎?

是這樣的(一個量化循環到一個數組中的所有元素鉗位到值沒有分支,使用AVX支持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]); 

我不想使用代碼噴射器。我想查看英特爾或AT風格x86指令AT &。

+0

如果你正在寫這樣的ASM(16位寄存器和使用'div' 4,而不是一個'SHR人,2'),[它絕對*不*會比什麼C編譯器可能更快make for you。](https://stackoverflow.com/questions/40354978/why-is-this-c-code-faster-than-my-hand-written-assembly-for-testing-the-collat​​/40355466# 40355466),所以你應該使用JNI與C或C++。如果您知道如何調整當前CPU的微體系結構,則ASM只對性能有用。這是一個有用的問題,但這個例子是爲什麼大多數人*不應該使用asm的例子。 – 2017-10-07 18:24:53

+0

你說得對。兩件事情在同一時間。如果我當時有足夠的經驗,我會按照適當的指令順序添加類似AVX點的產品。 – 2017-10-07 18:53:13

+0

您可以編輯問題以使用現代的東西。就像也許BMI2'pdep',它沒有Java內在。不過,理想情況下,您可以想出一些您不能輕鬆獲得C編譯器爲您發佈的東西。 – 2017-10-07 18:57:57

回答

13

Java代碼和底層硬件之間有一層抽象,原則上這種事情是不可能的;您在技術上無法知道代碼在底層機器上的表示方式,因爲相同的字節碼可以在不同的處理器和不同的體系結構上運行。

你正式的可以 do是使用Java Native Interface(JNI)從你的Java代碼中調用本地代碼。調用的開銷很大,並且與Java共享數據相當昂貴,所以這應該只用於體面大小的本地代碼塊。

理論上,這樣的擴展應該是可能的,但是。我們可以想象一個Java編譯器針對特定的平臺並允許程序集轉義。編譯器必須發佈它的ABI,所以你應該知道調用約定。不過,我不知道有這樣做。但有直接將Java編譯爲本地代碼的severalcompilersavailable;如果沒有我的瞭解,他們中的一個可能會支持這樣的事情,或者可能會擴展到這樣做。最後,在不同的層面上,JVM有字節碼彙編程序,如Jasmin.字節碼彙編程序允許您直接編寫針對JVM的「機器代碼」,有時您可以編寫比編譯程序javac更好的代碼生成。無論如何,玩起來都很有趣。

+0

好的。我們也會嘗試字節碼彙編器 – 2012-07-24 13:56:23

+2

在可用的提前Java到本地代碼編譯器中,[Excelsior JET](http://www.excelsiorjet.com)只實現JNI,而[GCJ](http:// gcc .gnu.org/java /)同時支持JNI和它自己的稱爲[CNI]的接口(http://gcc.gnu.org/onlinedocs/gcj/About-CNI.html)。 – 2012-07-26 10:57:30

2

你不能直接從Java調用程序集。但是您可以通過JNI調用C代碼,然後從那裏調用程序集。

This article shows how.

+0

非常好。我會嘗試。我正在使用數字火星編譯器。你認爲__asm有可能嗎?恩,我會嘗試自己。謝謝 – 2012-07-24 13:44:52

+0

據我記得,你可以使用任何你喜歡的c編譯器。 java只是使用平臺abi。 – 2012-07-24 13:45:45

+0

您可以在彙編中編寫一個遵循C ABI的函數,因此可以被稱爲與C函數相同的函數。基本上,無論你在C函數中如何使JNI兼容,你都可以在asm中完成。 – 2017-10-07 18:10:42

1

您使用JNI或JNA和從Java調用本機的功能。或者作爲替代方法,您將字節碼作爲InputStream並將Java類從中取出。

1

你也不妨看看Aparapi

+0

isnt aparapi爲GPU的並行編程? – 2012-07-28 14:08:14

+3

是的。你不是問如何更快地進行矩陣計算? – 2012-07-29 16:34:36

2

可以使用Machine Level Java技術從Java調用程序集。它將用Java編寫的彙編代碼透明地打包到本地庫中,但非常類似於最常用的彙編語法。接下來你只需要調用一個本地方法,即在同一個類中定義你的程序集的寫入位置。因此,您始終保持在Java環境中,無需從Java IDE切換到某些組裝工具,然後再切換回Java。

+0

看起來像您建議缺乏文檔的API。你能提供更多細節嗎? – 2014-12-31 00:58:39

+0

比jni更低的api /接口延遲? – 2014-12-31 21:06:11

5

您不能在Java代碼中直接內聯程序集。儘管如此,與其他答案所聲稱的相反,在不經過任何中介C(或C++)層的情況下方便地調用程序集是可能的。

演練

考慮下面的Java類:

public class MyJNIClass { 

    public native void printVersion(); 

} 

主要的想法是申報使用JNI命名約定的象徵。在這種情況下,彙編代碼中使用的重名名稱是Java_MyJNIClass_printVersion。該符號必須從其他翻譯單元可見,例如可以使用FASM中的public指令或NASM中的global指令來實現。

收件與目標結構的調用約定彙編代碼(參數可能在寄存器中被傳遞,在棧上,在其他的存儲器結構,等等)。傳遞給彙編函數的第一個參數是一個指向JNIEnv的指針,它本身是一個指向JNI函數表的指針。用它來調用JNI函數。例如,使用NASM和定位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 
    ... 

指標的JNI功能可以在Java documentation找到。由於JNI函數表基本上是一個指針數組,所以不要忘記將這些索引乘以目標架構中指針的大小。

傳遞給你的彙編函數的第二個參數是要調用Java類或對象的引用。所有後續參數都是您的本地Java方法的參數。

最後,組裝您的代碼以生成一個目標文件,然後從該目標文件創建一個共享庫(GCC可以使用類似於gcc -shared -o ...的命令執行最後一步)。

運行例如

我已經創建了一個fully runnable example on GitHub,隨意看看,玩它,以獲得更好的理解。

+0

所以這比JNI C++更深入? – 2017-05-11 22:17:33

+0

那麼它使用的是與C或C++相同的JNI實現,但是從較低級別實現。 ;-) – Pyves 2017-05-12 08:08:56

+1

你可以寫'mov rax,[rdi]'/'call [rax + 8 * 4]'。 x86尋址模式比額外的指令更有效率。內存間接調用不會比load + call更快,但也不會更慢,並且節省了代碼大小和解碼帶寬。 (嗯,實際上根據http://agner.org/optimize/,它在AMD上可能會比較慢,因爲它超過2個uops,這意味着VectorPath(微碼),而不是DirectPath。如果調整爲AMD,也許'mov rax ,[rdi]'/'mov rax,[rax + 8 * 4]'/'call rax'。仍然沒有ADD指令,總是更差) – 2017-10-07 18:16:33