14

我知道 Java 是一种安全的语言,但是当需要矩阵计算时,我可以尝试更快的方法吗?

我正在学习 C++、Digital-Mars 编译器和 FASM 中的 __asm{}。我想在 Java 中做同样的事情。如何在函数中内联汇编代码?这甚至可能吗?

像这样的东西(使用 CPU 的 AVX 支持将数组的所有元素钳制为一个值而不分支的向量化循环):

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  # TODO: use vminps instead
    vmovups    %ymm1, (%rdi, %rdx, 4)
    # TODO: unroll the loop a bit, and maybe handle unaligned output specially if that's common
    add         $32, %rdx
    cmp         %rcx, %rdx
    jb     .Lloop                  # } while(idx < count)
    vzeroupper
);

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

我不想使用代码注入器。我想查看 Intel 或 AT&T 风格的 x86 指令。

4

6 回答 6

20

在你的 Java 代码和底层硬件之间有一个抽象层,这使得这种事情在原则上是不可能的;从技术上讲,您无法知道代码在底层机器上是如何表示的,因为相同的字节码可以在不同的处理器和不同的架构上运行。

您正式可以做的是使用Java 本地接口(JNI) 从您的 Java 代码中调用本地代码。调用开销很大,并且与 Java 共享数据相当昂贵,因此这应该只用于相当大的本机代码块。

不过,理论上,这样的扩展应该是可能的。可以想象一个针对特定平台并允许程序集转义的 Java 编译器。编译器必须发布它的 ABI,所以你会知道调用约定。但是,我不知道有任何这样做。但是有几个 可用的编译器 可以将 Java 直接编译为本机代码。其中一个可能在我不知情的情况下支持这样的事情,或者可以扩展到这样做。

最后,在完全不同的层面上,JVM 有字节码汇编器,比如Jasmin。字节码汇编器允许您编写直接针对 JVM 的“机器代码”,有时您可以编写比javac编译器生成的代码更好的代码。无论如何,玩起来很有趣。

于 2012-07-24T13:42:30.230 回答
10

您不能在 Java 代码中直接内联汇编。然而,与其他一些答案所声称的相反,可以方便地调用程序集而不通过任何中间 C(或 C++)层。

快速演练

考虑以下 Java 类:

public class MyJNIClass {

    public native void printVersion();

}

主要思想是使用 JNI 命名约定来声明符号。在这种情况下,在您的汇编代码中使用的损坏名称是Java_MyJNIClass_printVersion. 该符号必须在其他翻译单元中可见,例如可以使用publicFASM 中的global指令或 NASM 中的指令来实现。如果您使用的是 macOS,请在名称前附加一个下划线。

使用目标架构的调用约定编写汇编代码(参数可以在寄存器、堆栈、其他内存结构等中传递)。传递给汇编函数的第一个参数是指向 的指针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 文档中找到。由于 JNI 函数表基本上是一个指针数组,因此不要忘记将这些索引乘以目标架构中指针的大小。

传递给汇编函数的第二个参数是对调用 Java 类或对象的引用。所有后续参数都是您的本机 Java 方法的参数。

最后,组装您的代码以生成一个目标文件,然后从该目标文件创建一个共享库。GCC 和 Clang 可以使用类似于gcc/clang -shared -o ....

其他资源

此 DZone 文章中提供了更全面的演练。我还在GitHub 上创建了一个完全可运行的示例,请随意查看并试用它以获得更好的理解。

于 2017-05-11T22:13:10.540 回答
2

可以使用机器级 Java技术从 Java 调用程序集。它将用 Java 编写但与最常用的汇编语法非常相似的汇编代码透明地打包到本机库中。接下来,您只需要调用在编写程序集的同一个类中定义的本机方法。因此,您始终停留在 Java 环境中,无需从 Java IDE 切换到某些汇编工具,然后再切换回 Java。

于 2014-12-31T00:31:47.260 回答
1

您可能还想看看Aparapi

于 2012-07-26T10:51:18.470 回答
1

您不能直接从 Java 调用程序集。但是你可以通过JNI调用 C 代码,然后你可以从那里调用汇编。

这篇文章展示了如何。

于 2012-07-24T13:43:16.310 回答
1

您使用 JNI 或 JNA 并从 Java 调用您的本机函数。或者作为替代方案,您将字节码作为 InputStream 并从中创建一个 Java 类。

于 2012-07-24T13:44:04.270 回答