【问题标题】:Possible increase of performance using JNI?使用 JNI 可能提高性能?
【发布时间】:2018-12-05 13:32:37
【问题描述】:

一两年前,我创建了一个用 Java 编写的程序来模拟 n 体问题。 最近我有了一个疯狂的想法,将程序重写为分布式程序,以便能够以更高的精度模拟更大的质量。

分析旧程序显示,正如预期的那样,大约 90% 的程序用于计算浮点类型值。如果我没记错的话,C/C++ 在做算术运算时比 Java 快很多,尤其是浮点类型计算。

无论如何,这是真正的问题:)

通过使用 JNI,我是否可以期望速度提高等于用 C/C++ 编写的程序(用于计算),还是 JVM 会减慢速度?

【问题讨论】:

    标签: java optimization java-native-interface


    【解决方案1】:

    大多数浮点操作在 Java 中大约需要 1 ns,所以我不确定您希望它们在 C++ 中的速度有多快。

    但是 JNI 调用通常需要大约 30 ns,因此除非您在每次调用中执行大量浮点运算,否则您将花费​​比节省更多的成本。

    正如下面的微基准所暗示的,一旦代码预热,每个操作都是亚纳秒。

    如果您希望它更快,您可以使用多个内核并使其速度提高 4 倍或更多。

    public static void main(String[] args) throws Exception {
        int length = 200000;
        double[] a = fill(new double[length]);
        double[] b = fill(new double[length]);
        double[] c = fill(new double[length]);
        double[] x = new double[length];
    
        for (int i = 0; i < 10; i++)
            testTime(length, a, b, c, x);
    }
    
    private static void testTime(int length, double[] a, double[] b, double[] c, double[] x) {
        long start = System.nanoTime();
        for (int i = 0; i < length; i++)
            x[i] = a[i] * b[i] + c[i];
        long time = System.nanoTime() - start;
        System.out.printf("Average time per double operation was %.1f ns%n", time / 2.0 / length);
    }
    
    private static double[] fill(double[] doubles) {
        for (int i = 0; i < doubles.length; i++)
            doubles[i] =  Math.random();
        return doubles;
    }
    

    打印

    Average time per double operation was 10.9 ns
    Average time per double operation was 17.9 ns
    Average time per double operation was 1.7 ns
    Average time per double operation was 1.0 ns
    Average time per double operation was 0.9 ns
    Average time per double operation was 0.8 ns
    Average time per double operation was 0.9 ns
    Average time per double operation was 0.8 ns
    Average time per double operation was 1.0 ns
    Average time per double operation was 0.9 ns
    

    【讨论】:

    • 程序已经是多线程的,这就是为什么我想让它分布式,我想要更多的 CPU 能力! :D 很好的答案,我不知道开销。也是展示 JIT 的好方法。
    • @David,您可以看到 Java 对于未预热的代码非常慢。我会确保任何测试至少运行 5 秒。
    【解决方案2】:

    我想这个答案:JNI Performance 也适用于此。如果您多次调用 JNI,则每次您的性能都会受到影响。如果您调用 JNI 进行繁重的计算,那么 C 优化的代码应该会执行得更快。

    【讨论】:

    • 确实,情况和我一样。几千个“有点”较小的计算。每次约 15 次算术运算。
    • 这很重要,尤其是当您在使用某些遗留 C 代码或移植到 Java 之间进行辩论时。 Java 可能含糊不清,因为您不知道 JVM 是否会内联性能关键函数 - JIT 运行良好,但您无法转储字节码并查看它是如何实现的。在 C 中,你得到你所看到的,想看看有没有内联的东西?转储程序集。根据我的经验,用于繁重计算的 JNI 是一个胜利。
    【解决方案3】:

    实际上,我最近使用 Java 编写的相同代码测试了分形生成 (Mandelbrot),然后移植到 C,令我惊讶的是,我发现使用 JNI 方法时计算速度略有下降。我只能用一件事来解释这种现象: 如果您使用 c 代码,则无法利用 HotSpot 对可重复计算的优化。

    您可以在以下位置自行查看示例代码: http://code.google.com/p/frgenjava/

    编辑: 在我描述的情况下,当使用 JNI 时,我忽略了 JNI 调用的开销,调用大约需要 20ns,即使这样 C 的执行速度也更慢。

    【讨论】:

    • 这是关于 JIT 编译的有趣之处之一 - 因为它可以针对您正在运行的 cpu 和系统进行编译,而无需考虑其他 cpu 需要什么,您有时可以获得性能提升与预编译代码。
    【解决方案4】:

    与性能有关的最重要的事情是测试和基准测试。您听说 C++ 在浮点方面比 java 更好。好的,这可能是这种情况。但是如果没有长凳来显示这种实际差异,这不值一分钱。这可能真的是假的。

    事实上,现代 java 使用 JIT。那是什么 ?好吧,我们都知道 java 使用字节码并且字节码是被解释的。这是真的,也是假的。事实上,大量使用的代码会即时编译为针对您的平台优化的本机代码。 JIT 甚至可以通过执行统计执行 C/C++ 中无法实现的优化。

    现在 Java 和 JVM 被广泛接受为真正快速有效的平台。人们开始在重计算时代真正使用它并取得了相当大的成功。在网格上部署也更容易。

    最近的基准测试倾向于显示类似于 C/C++ 的性能(例如 http://blogs.oracle.com/amurillo/entry/java_vs_c

    那么,您会从 JNI 中获益并仅移植到 C++。我会说不,或者收益很少。 (但如果您想确定,请再次测试)。如果不进行优化,C++ 版本可能会更慢。

    您能否通过使用 JNI 和使用优化的汇编代码(包括 SEE 指令)获得巨大的改进?如果你做对了,肯定是的。这需要大量的板凳、专业知识和时间。

    【讨论】:

      【解决方案5】:

      我无法评论 Java 算术运算的速度,但我知道 JNI 会直接调用你的 C++ 代码,所以你会得到本机速度,是的

      【讨论】:

        猜你喜欢
        • 2014-04-10
        • 1970-01-01
        • 2014-04-05
        • 1970-01-01
        • 1970-01-01
        • 2010-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多