Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 603282be authored by felkachang's avatar felkachang
Browse files

Benchmark throwing NPE between CriticalNative vs. FastNative

CriticalNative can't throw a NPE in native layer since JNIEnv isn't
accessible. FastNative can throw a NPE description in native layer.
It means that moving the affair from native layer to java layer is
the major effect of changing from FastNative to CriticalNative.

When an application wants to change the usage of FastNative to
CriticalNative. It needs the proof, metrics, impact for the following:
* throwing an exception in java or native layer.
    Experiment result for throwing an exception:
        FastNative:     ~4000 ns throw an exception in native layer
        CriticalNative: ~3000 ns throw an exception in java layer
* checking the condition in java or native layer.
    Experiment result for checking:
        FastNative:     ~12 ns to check the number in native layer
        CriticalNative:  ~6 ns to check the number in java layer

Bug: 173709508

Test: atest CorePerfTests:android.perftests.SystemPerfTest
Change-Id: Ifdbe0e9db881e0a21ec429844538e7166e558ac4
parent fe8bf190
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -9,7 +9,6 @@ package {

cc_library_shared {
    name: "libperftestscore_jni",
    sdk_version: "21",

    srcs: ["SystemPerfTest.cpp"],

@@ -20,4 +19,10 @@ cc_library_shared {
        "-Wunreachable-code",
    ],
    header_libs: ["jni_headers"],

    shared_libs: [
        "libandroid_runtime",
        "liblog",
        "libnativehelper",
    ],
}
+30 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

#include <jni.h>

#include "nativehelper/JNIHelp.h"
#include "core_jni_helpers.h"

#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))

static void jintarrayArgumentNoop(JNIEnv*, jclass, jintArray, jint) {
@@ -48,11 +51,38 @@ static jint jintarrayBasicAccess(JNIEnv* env, jclass, jintArray jarray, jint ind
    return ret;
}

static jint jintFastNativeAccess(JNIEnv*, jclass, jint number) {
    return number;
}

static jint jintCriticalNativeAccess(CRITICAL_JNI_PARAMS_COMMA jint number) {
    return number;
}

static jint jintFastNativeCheckNullPointer(JNIEnv* env, jclass, jint number) {
    if (number == 0) {
        jniThrowNullPointerException(env, NULL);
        return -1;
    }
    return number;
}

static jint jintCriticalNativeCheckNullPointer(CRITICAL_JNI_PARAMS_COMMA jint number) {
    if (number == 0) {
        return -1;
    }
    return number;
}

static const JNINativeMethod sMethods[] = {
    {"jintarrayArgumentNoop", "([II)V", (void *) jintarrayArgumentNoop},
    {"jintarrayGetLength", "([I)I", (void *) jintarrayGetLength},
    {"jintarrayCriticalAccess", "([II)I", (void *) jintarrayCriticalAccess},
    {"jintarrayBasicAccess", "([II)I", (void *) jintarrayBasicAccess},
    {"jintFastNativeAccess", "(I)I", (void *) jintFastNativeAccess},
    {"jintCriticalNativeAccess", "(I)I", (void *) jintCriticalNativeAccess},
    {"jintFastNativeCheckNullPointer", "(I)I", (void *) jintFastNativeCheckNullPointer},
    {"jintCriticalNativeCheckNullPointer", "(I)I", (void *) jintCriticalNativeCheckNullPointer},
};

static int registerNativeMethods(JNIEnv* env, const char* className,
+102 −0
Original line number Diff line number Diff line
@@ -22,8 +22,10 @@ import android.perftests.utils.PerfStatusReporter;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;

import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -106,6 +108,93 @@ public class SystemPerfTest {
        }
    }

    /** this result should be compared with {@link #testJniCriticalNativeAccess()}. */
    @Test
    public void testJniFastNativeAccess() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            jintFastNativeAccess(50);
        }
    }

    /**
     * This result should be compared with {@link #testJniFastNativeAccess()}.
     *
     * <p>In theory, the result should be better than {@link #testJniFastNativeAccess()}.
     */
    @Test
    public void testJniCriticalNativeAccess() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            jintCriticalNativeAccess(50);
        }
    }

    /** The result should be compared with {@link #testJniCriticalNativeCheckNullPointer()}. */
    @Test
    public void testJniFastNativeCheckNullPointer() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            final int echoNumber = jintFastNativeCheckNullPointer(50);
        }
    }

    /**
     * The result should be compared with {@link #testJniFastNativeCheckNullPointer()}.
     *
     * <p>CriticalNative can't reference JavaEnv in native layer. It means it should check the null
     * pointer in java layer. It's a comparison between native layer and java layer.
     */
    @Test
    public void testJniCriticalNativeCheckNullPointer() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            final int echoNumber = jintCriticalNativeCheckNullPointer(50);
            if (echoNumber == -1) {
                Assert.fail("It shouldn't be here");
            }
        }
    }

    /**
     * The result should be compared with {@link #testJniCriticalNativeThrowNullPointerException()}.
     */
    @Test
    public void testJniFastNativeThrowNullPointerException() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            try {
                jintFastNativeCheckNullPointer(0);
            } catch (NullPointerException e) {
                continue;
            }
            Assert.fail("It shouldn't be here");
        }
    }

    /**
     * The result should be compared with {@link #testJniFastNativeThrowNullPointerException()}.
     *
     * <p>CriticalNative can't reference JavaEnv in native layer. It means it should check the null
     * pointer in java layer. It's a comparison between native layer and java layer.
     */
    @Test
    public void testJniCriticalNativeThrowNullPointerException() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            try {
                final int echoNumber = jintCriticalNativeCheckNullPointer(0);
                if (echoNumber == -1) {
                    throw new NullPointerException();
                }
            } catch (NullPointerException e) {
                continue;
            }
            Assert.fail("It shouldn't be here");
        }
    }

    // ----------- @FastNative ------------------
    @FastNative
    private static native void jintarrayArgumentNoop(int[] array, int length);
    @FastNative
@@ -114,4 +203,17 @@ public class SystemPerfTest {
    private static native int jintarrayCriticalAccess(int[] array, int index);
    @FastNative
    private static native int jintarrayBasicAccess(int[] array, int index);

    @FastNative
    private static native int jintFastNativeAccess(int echoNumber);

    @FastNative
    private static native int jintFastNativeCheckNullPointer(int echoNumber);

    // ----------- @CriticalNative ------------------
    @CriticalNative
    private static native int jintCriticalNativeAccess(int echoNumber);

    @CriticalNative
    private static native int jintCriticalNativeCheckNullPointer(int echoNumber);
}