一尘不染

JVM的JIT编译器是否生成使用矢量化浮点指令的代码?

java

可以说,我的Java程序的瓶颈确实是一些紧密的循环,无法计算一堆矢量点积。是的,我已经进行了概要分析,是的,它是瓶颈,是的,它很重要,是的,这就是算法的方式,是的,我运行了Proguard来优化字节码,等等。

实质上,这是点产品。float[50]与之类似,我有两个,我需要计算成对乘积之和。我知道处理器指令集可以像SSE或MMX一样快速且批量地执行此类操作。

是的,我可能可以通过在JNI中编写一些本机代码来访问它们。JNI调用结果非常昂贵。

我知道您不能保证JIT将编译或不编译。有没有人 曾经
听说过使用这些指令的JIT生成的代码?如果是这样,那么有关Java代码的任何内容都可以使它以这种方式编译吗?

可能是“否”;值得一问。


阅读 265

收藏
2020-09-08

共1个答案

一尘不染

因此,基本上,您希望代码运行得更快。JNI就是答案。我知道您说这对您不起作用,但让我向您展示您错了。

这里是Dot.java

import java.nio.FloatBuffer;
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include = "Dot.h", compiler = "fastfpu")
public class Dot {
    static { Loader.load(); }

    static float[] a = new float[50], b = new float[50];
    static float dot() {
        float sum = 0;
        for (int i = 0; i < 50; i++) {
            sum += a[i]*b[i];
        }
        return sum;
    }
    static native @MemberGetter FloatPointer ac();
    static native @MemberGetter FloatPointer bc();
    static native @NoException float dotc();

    public static void main(String[] args) {
        FloatBuffer ab = ac().capacity(50).asBuffer();
        FloatBuffer bb = bc().capacity(50).asBuffer();

        for (int i = 0; i < 10000000; i++) {
            a[i%50] = b[i%50] = dot();
            float sum = dotc();
            ab.put(i%50, sum);
            bb.put(i%50, sum);
        }
        long t1 = System.nanoTime();
        for (int i = 0; i < 10000000; i++) {
            a[i%50] = b[i%50] = dot();
        }
        long t2 = System.nanoTime();
        for (int i = 0; i < 10000000; i++) {
            float sum = dotc();
            ab.put(i%50, sum);
            bb.put(i%50, sum);
        }
        long t3 = System.nanoTime();
        System.out.println("dot(): " + (t2 - t1)/10000000 + " ns");
        System.out.println("dotc(): "  + (t3 - t2)/10000000 + " ns");
    }
}

Dot.h

float ac[50], bc[50];

inline float dotc() {
    float sum = 0;
    for (int i = 0; i < 50; i++) {
        sum += ac[i]*bc[i];
    }
    return sum;
}

我们可以使用以下命令使用JavaCPP进行编译和运行:

$ java -jar javacpp.jar Dot.java -exec

使用2.80GHz @ Fedora 30,GCC 9.1.1和OpenJDK 8或11的Intel®Core™i7-7700HQ
CPU,可以获得以下输出:

dot(): 39 ns
dotc(): 16 ns

或大约快2.4倍。我们需要使用直接NIO缓冲区而不是数组,但是HotSpot可以像访问数组一样快地访问直接NIO缓冲区。另一方面,在这种情况下,手动展开循环不会显着提高性能。

2020-09-08