Rendimiento de Android Runtime vs NDK

Mientras desarrollaba un motor de juego para Android, confiaba en que el código nativo C / C ++ se ejecutaría más rápido que un código Java similar. Esta afirmación es cierta, pero no para las últimas versiones de Android. Para comprobar por qué sucede esto, decidí investigar un poco.





Para la prueba, se utilizó Android Studio 4.1.3: para Java Android SDK (API 30), para C / C ++ Android NDK (r21, compilador CLang). La prueba es bastante tonta y hace operaciones aritméticas en una matriz int en dos bucles anidados. Nada significativo y específico.





Aquí hay un método escrito en 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++ ( Java GC):





extern "C" JNIEXPORT void JNICALL Java_com_axiom_firstnative_MainActivity_calculateNative(
        JNIEnv* env,
        jobject,
        jint 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;
        }
    }

    // delete[] array;
}

      
      



, 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 time: 2 166 748 ns

C/C++ time: 396 729 ns (C/C++ 5 )





Prestigio K3 Muze (Android 8.1):

Java time: 3 477 001ns ( )

C/C++ time: 547 692ns (C/C++ 6 ),

Java 30-40% (?).





Samsung Galaxy S21 Ultra (Android 11):

Java time: 111 000ns

C/C++ time: 121 269ns

: Java 9% 40-50% C/C++ .





CLang (-O3)  C/C++ ~30-35% (Prestigio K3 Muze Android 8.1) Java , .





Smasung Galaxy S21 Ultra (Android 11) Java 10-20% /C++ CLang (-O3). ...





p.s. , , CPU.





, Java Android C/C++ ? ?

. Android Runtime Ahead-of-Time Java , Just-In-Time . . :





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.





Java NDK C/C++ ?

Me parece que para dispositivos antiguos con una máquina virtual Dalvik VM (hasta Android 7.0), definitivamente sí. En cuanto a los dispositivos más nuevos, con versiones de Android superiores a 7.0 (donde se usa el tiempo de ejecución ART), esto no tiene mucho sentido, a menos que sea un desarrollador experimentado de C / C ++ que comprenda profundamente cómo funciona la CPU y sea capaz de hacer optimizaciones mejores que el Android Runtime. Y el juego no vale la pena (efecto / esfuerzo), excepto en los siguientes casos:





  • Estás portando una aplicación C / C ++ existente a Android





  • Quieres usar bibliotecas C / C ++ que no están disponibles en Java





  • Quieres usar API que no están disponibles en el SDK de Android





PD: Si tiene alguna idea, me complacerá comentarla.








All Articles