How Android Runtime compiles Java more efficiently than the CLang C/С++ compiler (Android NDK)?

Issue

I was absolutely sure that C\C++ native code will run faster than Java code. And it is. My simple C/C++ benchmark (random arithmetic operations on int array) runs 5-7 times faster than the same Java code on an old tablet (Samsung Galaxy Tab E – Android 4.4.4 – Dalvik VM), but slower on recent devices with ART Prestigio K3 Muze (Android 8.1) & Samsung S21 Ultra (Android 11).

Why Android Runtime compiled code runs faster than native C/C++ code (Android NDK / JNI)?

Java code:

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

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

C/C++ code (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 i0; i<size; i++) {
        array[i]  i;
        for (jint j0; 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);

Results:

Samsung Galaxy Tab E (Android 4.4.4) – Java time: 2166748ns , C/C++ time: 396729 ns (C/C++ 5 times faster)

but

Prestigio K3 Muze (Android 8.1) on first start – Java time:3477001ns, C/C++ time: 547692ns (C/C++ 6 times faster), but after warm up Java runs 30-40% faster.

Samsung Galaxy S21 Ultra (Android 11) – Java time: 111000ns, C/C++ time: 121269ns (Java 9% faster on first start and 40-50% faster after warm up!!!)

Turning on CLang compiler optimization options (-O3) makes C/C++ run ~30-35% faster (Android 8.1) than Android Runtime optimized Java code. But still, on Android 11 ART optimized code runs 10-20% faster than CLang C/C++ optimized (-O3) native code. That is mind-blowing…

p.s. Both benchmarks run sequentially on one thread, so I suppose they use the same core.

Question:

How Android Runtime compile more efficient native code than CLang compiler?

Is there any performance advantage in writing native C/C++ code on recent Android OS versions?

Solution

How Android Runtime compile more efficient native code than CLang (Android NDK) C/C++ compiler?

The JIT compiler complements ART’s current ahead-of-time (AOT) compiler and improves runtime performance.

Although JIT and AOT use the same compiler with a similar set of optimizations, the generated code might not be identical. JIT makes use of runtime type information can do better inlining and makes on stack replacement (OSR) compilation possible, all of which generate slightly different code.

JIT compilation activities

Is there any performance advantage in writing native C/C++ code on recent Android OS versions?

Definitely yes for old Android devices. Old Android devices use Dalvik VM that interprets code. Java code will be 5-7 times slower than the same C/C++ code on Dalvik VM. But on ART, in most cases, Android Runtime profile-guided compilation generates much more efficient native code based on application execution statistics, but Java still ~30-35% slower than optimized C/C++ code on Android 8.1 and ~10-20% faster on Android 11.

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

Conclusion: In my humble opinion, there is no performance advantage in writing C/C++ code for recent Android devices (starting from v7.0 Nougat – ART VM), unless you’re an experienced C/C++ developer and deeply understand CPU architecture, so you can do optimization much better than Android Runtime does.

Also, Android NDK (C/C++) is still the only way for Android Developers to:

  1. Port your native C/C++ code to Android.
  2. Use C/C++ game engines and libraries (like Vulkan or TensorFlow).
  3. Use platform-specific APIs not available in Android SDK.

Answered By – Bolat Basheyev

Leave a Comment