Android NDKを使用してJava言語とC言語で速度比較をする

By Iguchi
このエントリをはてなブックマークに追加このエントリをdel.icio.usに追加このエントリをLivedoor Clipに追加このエントリをYahoo!ブックマークに追加このエントリをFC2ブックマークに追加このエントリをNifty Clipに追加このエントリをPOOKMARK. Airlinesに追加このエントリをBuzzurl(バザール)に追加このエントリをChoixに追加このエントリをnewsingに追加

今回はAndroid NDKを使用して、JavaコードとC言語で記述されたNativeコードで速度の比較を行おうと思います。

まずはNDKをインストールします。以下のリンクからndkをダウンロードします。

http://developer.android.com/intl/ja/sdk/ndk/1.6_r1/index.html

ダウンロードしてきたら以下を実行しインストールします。
$(NDKROOT)/build/host-setup.sh

これでインストールが完了です。簡単ですね。

早速、Javaのコードを書いて速度比較していきましょう。

今回は簡単に1億回足し算を行い、JavaとNativeで比較してみます。

以下はNativeで記述する関数になります。C言語で実装するため、ここでは宣言だけにします。

以下はNativeのライブラリをロードする部分になります。

ここまでで実行すると中身が無いので、以下のようにアプリケーションが落ちます。
Native実装なし

では、C言語でNative関数のadd()の中身を作成しましょう。プロジェクトにjni/calculate.cを作成します。

特殊なことはやっていないので、C言語で書いてもJavaと同じですね。関数名、引数、返り値が見慣れない形になってますが、今回は気にせずいきましょう!。今回の目的はJNIを使用して、JavaとC言語の速度比較をすることです。

次にAndroid.mkというものを作成します。C言語では馴染みがあると思いますがMakefileになります。JavaでいうAntのようなものです。これを使用して作成したC言語コードをビルドします。とりあえずC言語コードと同じ場所に置きます。

ここまでで、フォルダ構成は次のようになります。
jniフォルダ

次はApplication.mkというファイルを作ります。これもMakefileになります。
これは初めにインストールしたNDKディレクトリの下に置きます。今回の場合は、
$(NDKROOT)/apps/JniTest/Application.mkです。
これ、不思議だと思いませんか?。私ははじめは、自分のプロジェクトの下にないMakefileを使うのか理解できませんでした、プロジェクト以下にMakefileがないので移植性も悪くなるし・・。多分、理由はNDKをインストールしたディレクトリ直下にある、GNUmakefileを使いたいからだと思います。これを使用すると複雑なMakefileを作成しなくてよくなります。Makefileを自作出来る方であれば、これを使用しなくてもよいと思います。

今回は素直にNDKディレクトリ直下にApplication.mkを作る方法でいきます。以下のように記述します。

これでmakeの準備は完了です。NDKディレクトリ直下で以下のコマンドを実行します。先ほど書いたC言語コードがコンパイルされます。

以下のようなログをはきました。C言語の共有ライブラリが出来あがってますね。

プロジェクトをリフレッシュすると、/libs/armeabi/libcalculate.soが組み込まれます。
ライブラリ

あとは、いつも通り実行するだけです。では実行しましょう。
結果は以下です。
実行結果

Javaは45825ミリ秒、Nativeは9ミリ秒・・・・早い。早すぎる。

Javaは遅いということはよく聞いていたけれど、ここまで差が開くとは思いませんでした。演算結果もあっているので、きちんと動いていそうですし。

うーん。比較する計算式が簡単すぎて、C言語の方はコンパイル時に激しい最適化が行われている可能性があるので、計算式を以下のように変更してもう一度再測定してみました。

結果は次のようになりました。
再測定
Javaは17309ミリ秒、Nativeは1834ミリ秒で、答えも一致している。

だいぶ信憑性がある数字になったかな。演算方法でどのくらい早くなるかは変化するので、効果があるかどうかは実際に使用するプログラムで試さないとわかりませんが、少なからず効果はあるようです。

最後に、まとめとしてAndroid NDKを使用するために必要なファイルは最低以下の3つです。これがあればAndroid NDKを使用することができます。

  • C言語 or C++言語のソース
  • Android.mk
  • Application.mk
  • そしてAndroid NDKの使用はアプリケーションの高速化を行うための一つの手段になるということがわかったと思います。

    以上です、今回はここまでにします。

    関連する投稿


    5 Tweets

    1. yamada より:

      ネイティブって、すごい早いですね!
      NDKで作成したモジュールを実機に組み込む事ってできるんですか!?

    2. Big iguchi より:

      実機に組み込むこと出来ますよ。今回の検証もHT-03A上でやりました。
      C言語で作られたバイナリ(.soなど)もapkファイルの中に含まれます。
      ぜひ試してみてください!。

    3. 乳牛 より:

      最近のJVMだとHotSpotで最適化なので、呼出が1回こっきりだと最適化されていないのかもしれません。。。
      と、いいつつDalvikVMはJITすらまだ未実装(いまチャレンジ中みたいです)なので、HotSpotが載っているとは思えないので、上記の実験結果通りバイトコードをインタープリタ実行されるJavaのパフォーマンスがネイティブに比べて圧倒的に悪いという事なのでしょうね。

      ちょっと複雑なことをやるとNDKが必要になってきそうですね。勉強になりました。

    4. 参考になりました より:

      参考にさせていただきました。
      結果が10のほうですが、Xperiaで走らせたところ軒並み10倍~20倍程度高速でした。

    5. 参考になりました より:

      おっと。お礼を忘れておりました・・失礼しました。
      ありがとうございました。

    6. k より:

      longだからじゃない?

    7. 匿名 より:

       おそらくCの方のsizeof(long)をしたら32bitになってんじゃないですか?
       32bit CPUでlongの演算するときFPUを使ったり(CPUによって色々)するんで結構
      その差が激しいと思います。

       せっかくネイティブキットを使うなら単純な演算より、SIMD(CPUにあるなら)や
      スタック変数とポインタの駆使でしょうね。この辺りの差はPCでやっても体感出きるので
      デカいと思います。

    8. 匿名 より:

      たぶんこれ、classファイルがJVMにロードされる時間も含まれてるんじゃないですか?
      これじゃ実行時間(演算速度です)は比較できませんよ。
      ターンアラウンドタイムの比較でしたらこの記事の通りですが。

      Javaが速いケースってーのはWEBみたいに常にJVM(メモリ)上でスタンバイされているとき。

    9. 匿名 より:

      なかなか興味深いですね。
      一つ気になるのは(JavaもAndroidも詳しくないので)

      > System.loadLibrary(“calculate”);

      この部分のオーバーヘッドは影響無いのでしょうか?
      まぁこれを含めても十分速いみたいですが。(^^;)

    10. 匿名 より:

      long について勉強すべき。(C->32bit, Java->64bit)
      でもまぁ世の中のプログラマーはみんなこんなレベルなんだろう。
      あと HotSpot も考慮してあげるべき。
      関数化して何回か事前に呼んであげる。
      実際のアプリでも HotSpot の効果は高い。

    11. 匿名 より:

      性能比較になってないね。
      クラスライブラリを読み込む時間の考慮が無いし、
      実行時コンパイルが効く前で性能計って意味があるか?

      実行時コンパイルが効けば、Javaプログラムもネイティブコードで動作するので、
      レイテンシもスループットもC++プログラムとそん色ないが、

    12. 匿名 より:

      JITやhotspotに言及してる人居るけど、この記事の2010/3てまだ2.2も発表してない頃なんだし
      そのころのDalvikには無いんじゃねーの?

    Leave a Reply

    Additional comments powered by BackType