Skip to content

Commit

Permalink
JNI benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
ivnsch committed Jul 6, 2020
1 parent 40895fb commit d1368be
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 0 deletions.
4 changes: 4 additions & 0 deletions android/core/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

kotlinOptions {
jvmTarget = '1.8'
}
}

preBuild.doFirst {
Expand Down
158 changes: 158 additions & 0 deletions android/core/core/src/androidTest/java/org/coepi/core/JniBenchmarks.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package org.coepi.core

import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.coepi.core.jni.BenchmarksIntClass
import org.coepi.core.jni.JniApi
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/

// NOTE: Ideally this should use benchmark runner
// https://developer.android.com/studio/profile/build-benchmarks-without-gradle
// The documentation is incomplete, though, and it was consuming too much time
// so for now "manual" benchmarks

@Ignore("Benchmarks")
@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
class JniBenchmarks {

private val jniApi = JniApi()
private val nonJniApi = NonJniApi()

@Test
fun benchmarkNoopWithJni() {
// 70ms, 97ms, 80ms, 85ms
benchmark("benchmarkNoop") {
for (i in 0..1000000) {
jniApi.noopForBenchmarks()
}
}
}

@Test
fun benchmarkNoopWithoutJni() {
// 22ms, 22ms, 21ms, 22ms
benchmark("benchmarkNoopWithoutJni") {
for (i in 0..1000000) {
nonJniApi.noopForBenchmarks()
}
}
}

@Test
fun benchmarkSendReceiveIntWithJni() {
// 72ms, 79ms, 89ms, 90ms
benchmark("benchmarkSendReceiveIntWithJni") {
for (i in 0..1000000) {
jniApi.sendReceiveIntForBenchmarks(1)
}
}
}

@Test
fun benchmarkSendReceiveIntWithoutJni() {
// 22ms, 10ms, 23ms, 23ms
benchmark("benchmarkSendReceiveIntWithoutJni") {
for (i in 0..1000000) {
nonJniApi.sendReceiveIntForBenchmarks(1)
}
}
}

@Test
fun benchmarkSendReceiveStringWithJni() {
// 4596ms, 4343ms, 4561ms, 4360ms
benchmark("benchmarkSendReceiveStringWithJni") {
for (i in 0..1000000) {
jniApi.sendCreateStringForBenchmarks("hello")
}
}
}

@Test
fun benchmarkSendReceiveStringDontUseInputWithJni() {
// 2415ms, 2439ms, 2415ms, 2455ms
benchmark("sendCreateStringDontUseInputForBenchmarks") {
for (i in 0..1000000) {
jniApi.sendCreateStringDontUseInputForBenchmarks("hello")
}
}
}

@Test
fun benchmarkSendReceiveStringWithoutJni() {
// 12ms, 31ms, 27ms, 27ms
benchmark("benchmarkSendReceiveStringWithoutJni") {
for (i in 0..1000000) {
nonJniApi.sendCreateStringForBenchmarks("hello")
}
}
}

@Test
fun benchmarkSendClassWithJni() {
// 8639ms, 8690ms, 8476ms, 8694ms
benchmark("benchmarkClassStructWithJni") {
for (i in 0..1000000) {
jniApi.sendClassForBenchmarks(BenchmarksIntClass(1))
}
}
}

@Test
fun benchmarkSendClassWithoutJni() {
// 39ms, 31ms, 32ms, 41ms
benchmark("benchmarkClassStructWithoutJni") {
for (i in 0..1000000) {
nonJniApi.sendClassForBenchmarks(BenchmarksIntClass(1))
}
}
}

@Test
fun benchmarkReturnClassWithJni() {
// 37929ms, 39027ms, 38815ms, 38711ms
benchmark("benchmarkReturnClassWithJni") {
for (i in 0..1000000) {
jniApi.returnClassForBenchmarks()
}
}
}

@Test
fun benchmarkReturnClassWithoutJni() {
// 16ms, 40ms, 41ms, 30ms
benchmark("benchmarkReturnClassWithoutJni") {
for (i in 0..1000000) {
nonJniApi.returnClassForBenchmarks()
}
}
}

private fun benchmark(label: String, f: () -> Unit) {
val tsLong = System.currentTimeMillis()
f()
val ttLong = System.currentTimeMillis() - tsLong
println("$label took: ${ttLong}ms")
}
}

private class NonJniApi {
fun noopForBenchmarks() {}

fun sendReceiveIntForBenchmarks(i: Int): Int = 1

fun sendCreateStringForBenchmarks(string: String): String = "Return string"

fun sendClassForBenchmarks(c: BenchmarksIntClass) {}

fun returnClassForBenchmarks() = BenchmarksIntClass(1)
}
19 changes: 19 additions & 0 deletions android/core/core/src/main/java/org/coepi/core/jni/JniApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,28 @@ class JniApi {

external fun testReturnMultipleAlerts(): JniAlertsArrayResult

// Benchmarks

external fun noopForBenchmarks()

external fun sendReceiveIntForBenchmarks(i: Int): Int

external fun sendClassForBenchmarks(c: BenchmarksIntClass): Int

external fun returnClassForBenchmarks(): BenchmarksIntClass

external fun sendCreateStringForBenchmarks(string: String): String

// Doesn't do anything with the input string
external fun sendCreateStringDontUseInputForBenchmarks(string: String): String

/////////////////////////////////////////////////////////////////////////////////
}

data class BenchmarksIntClass(
val myInt: Int
)

data class FFIParameterStruct(
val myInt: Int,
val myStr: String,
Expand Down
68 changes: 68 additions & 0 deletions src/android/ffi_benchmarks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
extern crate jni;
use self::jni::JNIEnv;
use jni::objects::{JClass, JObject, JString, JValue};
use jni::sys::{jint, jobject, jstring};

#[no_mangle]
pub unsafe extern "C" fn Java_org_coepi_core_jni_JniApi_noopForBenchmarks(env: JNIEnv, _: JClass) {}

#[no_mangle]
pub unsafe extern "C" fn Java_org_coepi_core_jni_JniApi_sendReceiveIntForBenchmarks(
env: JNIEnv,
_: JClass,
i: jint,
) -> jint {
1
}

#[no_mangle]
pub unsafe extern "C" fn Java_org_coepi_core_jni_JniApi_sendCreateStringForBenchmarks(
env: JNIEnv,
_: JClass,
string: JString,
) -> jstring {
let string: String = env
.get_string(string)
.expect("Couldn't create java string")
.into();

let output = env
.new_string("Return string")
.expect("Couldn't create java string");

output.into_inner()
}

#[no_mangle]
pub unsafe extern "C" fn Java_org_coepi_core_jni_JniApi_sendCreateStringDontUseInputForBenchmarks(
env: JNIEnv,
_: JClass,
string: JString,
) -> jstring {
let output = env
.new_string("Return string")
.expect("Couldn't create java string");

output.into_inner()
}

#[no_mangle]
pub unsafe extern "C" fn Java_org_coepi_core_jni_JniApi_sendClassForBenchmarks(
env: JNIEnv,
_: JClass,
my_struct: JObject,
) {
let my_int_j_value_res = env.get_field(my_struct, "myInt", "I");
let my_int: i32 = my_int_j_value_res.unwrap().i().unwrap();
}

#[no_mangle]
pub unsafe extern "C" fn Java_org_coepi_core_jni_JniApi_returnClassForBenchmarks(
env: JNIEnv,
_: JClass,
) -> jobject {
let cls = env.find_class("org/coepi/core/jni/BenchmarksIntClass");
let my_int_j_value = JValue::from(123);
let obj = env.new_object(cls.unwrap(), "(I)V", &[my_int_j_value]);
obj.unwrap().into_inner()
}
1 change: 1 addition & 0 deletions src/android/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod android_interface;
pub mod ffi_benchmarks;
pub mod ffi_for_sanity_tests;
pub mod jni_domain_tests;

0 comments on commit d1368be

Please sign in to comment.