Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JNI benchmarks #119

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
// 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;