【问题标题】:How Android Runtime compiles Java more efficiently than the CLang C/С++ compiler (Android NDK)?Android Runtime 如何比 CLang C/С++ 编译器 (Android NDK) 更有效地编译 Java?
【发布时间】:2021-07-29 02:14:09
【问题描述】:

我绝对确信 C\C++ 原生代码将比 Java 代码运行得更快。它是。我的简单 C/C++ 基准测试(int 数组上的随机算术运算)比旧平板电脑(Samsung Galaxy Tab E - Android 4.4.4 - Dalvik VM)上的相同 Java 代码运行速度快 5-7 倍,但在最近的设备上运行速度较慢ART Prestigio K3 Muze(Android 8.1)和三星 S21 Ultra(Android 11)。

为什么 Android Runtime 编译的代码比原生 C/C++ 代码(Android NDK / JNI)运行得更快?

Java 代码

public void calculateJava(int size) {
    int[] array = new int[size];
    int sum = 0;

    for (int i=0; i<size; i++) {
        array[i] = i;
        for (int j=0; j<size; j++) {
            sum += array[i] * array[j];
            sum -= sum / 3;
       }
    }    
 }

C/C++ 代码 (JNI)

extern "C" JNIEXPORT void JNICALL Java_com_axiom_firstnative_MainActivity_calculateNative(
        JNIEnv* env,
        jobject,
        jint size) {

    int* array = new int[size];
    jint sum = 0;

    for (jint i=0; i<size; i++) {
        array[i] = i;
        for (jint j=0; j<size; j++) {
            sum += array[i] * array[j];
            sum -= sum / 3;
        }
    }

    // delete[] array;
}

OnClick (Java)

     long startTime = System.nanoTime();
     calculateNative(4096);
     long nativeTime = System.nanoTime() - startTime;
                
     startTime = System.nanoTime();
     calculateJava(4096);
     long javaTime = System.nanoTime() - startTime;
                
     String report = "VM:" + System.getProperty("java.vm.version")
                        + "\n\nC/C++: " + nativeTime 
                        + "ns\nJava: " + javaTime + "ns\n"
                        + "\nJava to C/C++ ratio " 
                        + ((double) javaTime / (double) nativeTime);

结果

Samsung Galaxy Tab E (Android 4.4.4) - Java 时间:2166748ns,C/C++ 时间:396729 ns(C/C++ 快 5 倍)

但是

首次启动时 Prestigio K3 Muze (Android 8.1) - Java 时间:3477001ns,C/C++ 时间:547692ns(C/C++ 快 6 倍),但预热后 Java 运行速度快 30-40%。

Samsung Galaxy S21 Ultra (Android 11) - Java 时间:111000ns,C/C++ 时间:121269ns(Java 首次启动快 9%,预热后快 40-50%!!! )

启用 CLang 编译器优化选项 (-O3) 可使 C/C++ 的运行速度 (Android 8.1) 比 Android 运行时优化的 Java 代码快约 30-35%。但是,在 Android 11 上,ART 优化代码的运行速度比 CLang C/C++ 优化 (-O3) 本机代码快 10-20%。这真是令人兴奋......

附言两个基准测试都在一个线程上按顺序运行,所以我想它们使用相同的内核。

问题

Android Runtime 如何编译出比 CLang 编译器更高效的原生代码?

在最新的 Android 操作系统版本上编写本机 C/C++ 代码是否有任何性能优势?

【问题讨论】:

  • 较新的手机具有分速 CPU - 您需要检查您的内核在哪个内核上运行。
  • 我无法理解你的结论:如果 Java 需要 396729ns=397µs 和 C++ 2166748ns=2167µs,为什么你说 C++ 更快?它需要更长的时间。 (第二组数字相同。)
  • 我在统计上犯了一个错误。反之亦然 (Java/C++) 在前两个示例中。固定。
  • 我找到了关于该主题的研究:“为了回答研究问题,根据实验结果,我们可以看到,在所选择的算法中,最快的算法之间没有显着的性能差异Java 的 FFT 库及其在本机代码中的相应实现。本机代码中的优化确实使本机代码比最快的 Java 实现快得多。这确实增加了代码的复杂性并降低了设备之间的兼容性。 diva-portal.org/smash/get/diva2:1109257/FULLTEXT01.pdf
  • 您应该发布完整的基准代码以获得更好的答案,因为缺少很多细节。例如,您没有提到 C++ 的优化级别,就像@RichardCritten 提到您是否在同一个核心上运行测试。此外,您在返回之前调用了 delete[],这意味着 C++ 函数的行为不同。

标签: java android c++ c android-ndk


【解决方案1】:

Android Runtime 如何编译出比 CLang (Android NDK) C/C++ 编译器更高效的原生代码?

JIT 编译器补充了 ART 当前的提前 (AOT) 编译器并提高运行时性能。

虽然 JIT 和 AOT 使用相同的编译器,但具有相似的一组 优化,生成的代码可能不完全相同。 JIT 使 使用运行时类型信息可以更好地进行内联和 make on 堆栈替换(OSR)编译可能,所有这些都生成 代码略有不同。

在最新的 Android 操作系统版本上编写本机 C/C++ 代码是否有任何性能优势?

对于旧的 Android 设备来说绝对可以。旧的 Android 设备使用解释代码的 Dalvik VM。 Java 代码将比 Dalvik VM 上的相同 C/C++ 代码慢 5-7 倍。但在 ART 上,在大多数情况下,Android 运行时配置文件引导的编译会根据应用程序执行统计生成更高效的本机代码,但 Java 仍然比 Android 8.1 和 ~10 上的优化 C/C++ 代码慢约 30-35%在 Android 11 上速度提高 -20%

https://source.android.com/devices/tech/dalvik/jit-compiler

结论: 在我看来,为最近的 Android 设备(从 v7.0 Nougat - ART VM 开始)编写 C/C++ 代码并没有性能优势,除非你是一位经验丰富的 C/C++ 开发人员并且深入了解 CPU 架构,所以你可以比 Android Runtime 做得更好。

此外,Android NDK (C/C++) 仍然是 Android 开发人员的唯一途径:

  1. 将您的原生 C/C++ 代码移植到 Android。
  2. 使用 C/C++ 游戏引擎和库(如 Vulkan 或 TensorFlow)。
  3. 使用 Android SDK 中不可用的特定于平台的 API。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    • 2018-11-26
    • 1970-01-01
    • 2019-05-31
    • 1970-01-01
    • 1970-01-01
    • 2020-01-19
    相关资源
    最近更新 更多