首页 > 解决方案 > Android 运行时如何比 CLang C/С++ 编译器 (Android NDK) 更有效地编译 Java?

问题描述

我绝对确信 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);

结果

三星 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%。

三星 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%。真是令人心旷神怡……

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

问题

Android 运行时如何编译比 CLang 编译器更高效的原生代码?

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

标签: javaandroidc++candroid-ndk

解决方案


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

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

尽管 JIT 和 AOT 使用具有相似优化集的相同编译器,但生成的代码可能并不相同。JIT 利用运行时类型信息可以更好地进行内联,并使堆栈替换 (OSR) 编译成为可能,所有这些都会生成略有不同的代码。

JIT 编译活动

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

对于旧的 Android 设备来说绝对是的。旧的 Android 设备使用解释代码的 Dalvik VM。Java 代码将比 Dalvik VM 上的相同 C/C++ 代码慢 5-7 倍。但在 ART 上,在大多数情况下,Android Runtime profile-guided 编译会根据应用程序执行统计生成更高效的本机代码,但 Java 仍然比 Android 8.1 上的优化 C/C++ 代码慢约 30-35% 和约 10-20%在 Android 11 上更快

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

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

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

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

推荐阅读