おはようございます。開発部の庄子です。
お仕事で初めてAndroid NDKに触れる機会があり、C++のネイティブコードがJavaコードに比べてどのくらい早いのか知りたいと思って、速度比較の実験してみました。
せっかくなのでブログネタにします。
検証環境
- Mac macOS 10.13
- Android Studio 2.3.3
- Gradle 3.3
- Gradle Plugin 2.3.3
- JDK 8
- 端末 Nexus5 (Android 6.0.1)
比較した処理
指定した数値以下で、最大の素数を表示する処理を書きました。
あまり効率を考えないでババっと書いたので、あまり中身で叩かないでください…
まずネイティブコード(C++)です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <jni.h> extern "C" JNIEXPORT jlong JNICALL Java_jp_co_sria_ndktest03_MainActivity_getPrimeNumbersNative(JNIEnv *env, jobject thiz, jlong jlimit) { long limit = (long)jlimit; long max = 2; for (long i = 2; i <= limit; i++) { bool isPrime = true; for (long j = 2; j < i; j++) { if (i % j == 0) { isPrime = false; break; } } if (isPrime && max <= i) { max = i; } } return max; } |
次にJavaで書いたコードがこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public long getPrimeNumbers(long limit) { long max = 2; for (long i = 2; i <= limit; i++) { boolean isPrime = true; for (long j = 2; j < i; j++) { if (i % j == 0) { isPrime = false; break; } } if (isPrime && max <= i) { max = i; } } return max; } |
booleanのキーワードとJNI関連のコード以外は同じですね。
ちなみにJavaから呼び出す処理はこんな感じです。画面に配置したボタンのクリックイベントで計測しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Override public void onClick(View view) { long limit = 10000L; // チェック対象の数値 long startMSec = System.currentTimeMillis(); //(1) long max; if (native) { max = getPrimeNumbersNative(limit); } else { max = getPrimeNumbers(limit); } long endMSec = System.currentTimeMillis(); //(2) textViewPrime.setText(String.valueOf(limit) + "の最大素数 : " + String.valueOf(max)); textViewTime.setText("処理時間 : " + String.valueOf(endMSec - startMSec) + "ミリ秒"); } |
(2)の計測時間から(1)の計測時間を引いて、処理時間(ms)を画面に表示します。
変数nativeを切り替えてC++とJavaそれぞれで計測しました。
結果
ネイティブ側が4倍近く早い結果になりました。
正直、ネイティブコード側はJNI呼び出しのオーバーヘッドがあると思うので、軽い処理ではそこまで変わらないのかなと思っていましたが、効果あるようです。
今後Androidアプリで速度が求められる処理を書く際、積極的にネイティブコードを活用していきます!(C/C++好きだし)