インテル® Atom™ プロセッサー・ベースのプラットフォームにおける Android* アプリケーションの開発と最適化
この記事は、インテル® デベロッパー・ゾーンに掲載されている「Android* Application Development and Optimization on the Intel® Atom™ Platform」の日本語参考訳です。
概要
ここでは、インテル® Atom™ プロセッサー・ベースのプラットフォームにおける Android* アプリケーションの開発および移植方法について説明し、Android* Native Development Kit (NDK) を使用する開発とパフォーマンス最適化の一般的な手法について述べます。この記事は、Android* 開発者がインテル® アーキテクチャー向けの高品質な アプリケーションを作成する際のリファレンスとして利用できます。
1. Android* アプリケーションの分類
Android* アプリケーションは、図 1 のように 2 種類に分類できます。
- Dalvik アプリケーション。Java* コードを含み、Android* SDK の API のみを使用し、xml や png などの必要なリソースファイルが APK ファイルにコンパイルされています。
- Android* NDK アプリケーション。Java* コード、リソースファイル、C/C++ ソースコード、そして (場合によっては) アセンブリー・コードを含んでいます。すべてのネイティブコードはダイナミック・リンク・ライブラリー (.so ファイル) にコンパイルされ、JNI メカニズムによりメインプログラムから Java* 経由で呼び出されます。
図 1: 2 種類の Android* アプリケーション
2. Android* Native Development Kit
2.1 はじめに
Android* Native Development Kit (NDK) は、Android* SDK のコンパニオン・ツールです。次の点から、NDK は Android* アプリケーションの開発における強力なツールと言えます。
- アプリケーションのパフォーマンスに重大な影響を与える部分をネイティブコードで作成できます。Java* コードを使用する場合、Java* ベースのソースコードは仮想マシンを使用してマシン言語に変換する必要があります。一方、ネイティブコードは、実行前にバイナリーへ直接コンパイルされ、最適化されます。ネイティブコードを適切に使用することで、ハードウェアによるビデオのエンコード/デコード、グラフィック処理、算術演算など、高いパフォーマンスのコードを作成できます。
- 既存のネイティブコードを再利用できます。C/C++ コードを、JNI メカニズムを使用して Java* コードから呼び出し可能なダイナミック・リンク・ライブラリーにコンパイルできます。
2.2 ツールの概要
開発期間中に、インテル® Hardware Execution Manager (インテル® HAXM) を使用して、Android* シミュレーターのパフォーマンスを向上できます。HAXM は、インテル® バーチャライゼーション・テクノロジー (インテル® VT) を使用するハードウェア支援による仮想エンジン (ハイパーバイザー) で、ホストマシン上で Android* アプリケーションのエミュレーションを高速化します。インテルから提供されている Android* x86 エミュレーター・イメージと公式の Android* SDK Manager を組み合わせて、インテル® HAXM はインテル® VT 対応システム上でより高速に Android* アプリケーションのエミュレーションを行います。インテル® HAXM に関する詳細は、http://software.intel.com (英語) を参照してください。
2.3 HAXM のインストール
Android* SDK Manager を使用して HAXM をインストールするか (推奨)、インテルの Web サイトからインストーラーをダウンロードして手動で HAXM をインストールします。自動更新を行う場合は、図 2 のように Android* SDK Manager を使用してインストールしてください。[1]
図 2: Android* SDK Manager を使用するインテル® HAXM のインストール
適切なインストール・パッケージを http://www.intel.com/software/android (英語) からホスト・プラットフォームにダウンロードし、手順に従ってインストールすることもできます。
2.3.1 HAXM のセットアップ
HAXM の実行には、インテルから提供されている Android* x86 システムイメージが必要です。このシステムイメージは、Android* SDK Manager を使用してダウンロードすることも、インテル® デベロッパー・ゾーン (英語) から手動でダウンロードすることもできます。
イメージをインストールすると、Android* SDK の “emulator-x86” バイナリーを使用して、インテル® x86 Android* エミュレーター・イメージが自動的に実行されます。Android* エミュレーターはインテル® VT により高速化され、開発プロセスをスピードアップします。
3. インテル® Atom™ アーキテクチャー向け NDK アプリケーションの開発および移植
3.1 インテル® Atom™ プロセッサー・ベースのデバイス向け NDK アプリケーションの開発
NDK をインストールしたら、NDK のメカニズムと使用法を理解するため、/docs/ ディレクトリーにあるドキュメント、(特に OVERVIEW.html と CPU-X86.html) をお読みください。
NDK アプリケーションの開発は、図 3 に示すように 5 つのステップで構成されます。
図 3: NDK アプリケーションの開発プロセス
ここでは、hello-jni デモを使用して 5 つのステップを示しています。このデモは NDK Root\samples\hello-jni フォルダーにあります [5]。hello-jni デモは NDK に含まれるサンプル・アプリケーションで、共有ライブラリーのネイティブメソッドから文字列を取得し、アプリケーション UI でそれを使用します。
3.1.1.ネイティブコードの作成
新しい Android* プロジェクトを作成し、/jni/ 以下にネイティブ・ソース・コードを配置します。プロジェクトのコンテンツを図 4 に示します。このデモには、ネイティブコードで記述された Java_com_example_hellojni_HelloJni_stringFromJNI() という名前の関数が含まれています。ソースコードから分かるように、この関数は JNI から文字列を返します。
図 4: ネイティブコードの作成
3.1.2 MakeFile ‘Android.mk’ の作成
デフォルトでは、NDK アプリケーションは ARM プラットフォーム向けにビルドされます。インテル® Atom™ プロセッサー・ベースのプラットフォーム向けにビルドするには、MakeFile に「APP_ABI := x86」を追加する必要があります。
図 5: MakeFile の作成
3.1.3 ネイティブコードのコンパイル
プロジェクト・ディレクトリーから ‘ndk-build’ スクリプトを実行してネイティブコードをビルドします。スクリプトはトップレベルの NDK ディレクトリーにあります。図 6 に結果を示します。
図 6: コンパイル済みのネイティブコード
ビルドツールは、自動でプロジェクト・ディレクトリー内の適切な場所にコンパイル済みの共有ライブラリーをコピーします。
3.1.4 Java* からのネイティブコードの呼び出し
共有ライブラリーを配置すると、Java* 側から関数を呼び出せます。図 7 にコードを示します。Java* コードでパブリックネイティブ関数呼び出し stringFromJNI() が作成され、System.loadlibrary() により共有ライブラリーがロードされます。
図 7: Java* からのネイティブコードの呼び出し
3.1.5 GDB によるデバッグ
GDB で NDK アプリケーションをデバッグする場合は、次の条件を満たしていなければなりません。
- NDK アプリケーションが ‘ndk-build’ を使用してビルドされている
- Android.manifest で NDK アプリケーションが ‘debuggable’ に設定されている
- NDK アプリケーションは Android* 2.2 (またはそれ以上) 上で実行される
- 1 つのターゲットのみ実行している
- adb のディレクトリーが PATH に設定されている
ndk-gdb コマンドを使用してアプリケーションをデバッグします。図 8 に示すように、ブレークポイントを設定するか、ステップ・バイ・ステップでデバッグを行い、変数値の変更履歴を追跡することができます。
図 8: GDB による NDK アプリケーションのデバッグ
3.2 インテル® Atom™ プロセッサー・ベースのデバイスへの既存の NDK アプリケーションの移植
このセクションでは、ARM プラットフォーム向けの既存の Android* アプリケーションを、インテル® Atom™ プロセッサー・ベースのプラットフォームに移植する場合について説明します。
Android* アプリケーションをインテル® Atom™ プロセッサー・ベースのデバイスへ移植するプロセスは、開発プロセスに似ています。図 9 に手順を示します。
図 9: インテル® Atom™ プロセッサー・ベースのプラットフォームへの Android* アプリケーションの移植
3.2.1 Dalvik アプリケーションの移植
Dalvik アプリケーションは、インテル® Atom™ プロセッサー・ベースのデバイスで直接実行できます。ユーザー・インターフェイスはターゲットデバイス用に調整する必要があります。タブレットなどの高解像度 (1280 x 800 以上) のデバイスの場合、デフォルトのメモリー割り当てではアプリケーションの要件を満たせず、アプリケーションを起動できない可能性があります。高解像度のデバイスでは、デフォルトのメモリー割り当てを増やすことを推奨します。
3.2.2 Android* NDK アプリケーションの移植
NDK アプリケーションの移植は、Dalvik アプリケーションの移植よりもやや複雑です。すべての NDK アプリケーションは、ネイティブコードの次の特性に基づいて 3 種類に分類することができます。
- ハードウェアに関連しない C/C++ コードのみを含んでいる
- サードパーティーのダイナミック・リンク・ライブラリーを使用している
- IA 以外のプラットフォームと関連性が高いアセンブリー・コードを含んでいる
ハードウェアに関連しない C/C++ コードのみを含むネイティブコード
- インテル® Atom™ プロセッサー・ベースのプラットフォーム上でアプリケーションを実行するには、ネイティブコードを再コンパイルします。
- NDK プロジェクトを開き、Android.mk ファイルに APP_ABI := armeabi armeabi-v7a x86 を追加して、ネイティブコードを “ndk-build” でリビルドします。
- Android.mk ファイルが見つからない場合は、次のコマンドを使用してプロジェクトをビルドします:
$ ndk-build APP_ABI=”armeabi armeabi-v7a x86″
- サポートされている x86 プラットフォームでアプリケーションを再パッケージ化します。
ネイティブコードがサードパーティーのダイナミック・リンク・ライブラリーを使用している場合は、共有ライブラリーをインテル® Atom™ プロセッサー・ベースのプラットフォーム向けの x86 バージョンに再コンパイルする必要があります。
ネイティブコードに IA 以外のプラットフォームと関連性が高いアセンブリー・コードが含まれている場合は、コードを IA のアセンブリー言語または C/C++ 言語で書き直す必要があります。
4. 最も一般的なネイティブコードの開発手法
4.1 メモリー・アライメントの強制
アーキテクチャー、プラットフォーム、コンパイラー間の違いにより、同じデータ構造であっても異なるプラットフォームではデータサイズが異なることがあります。メモリー・アライメントを強制しなければ、データサイズの不整合により読み込みエラーが発生する可能性があります。[2]
次の例では、異なるプラットフォームにおける同じデータ構造のデータサイズを説明します。
struct TestStruct {
int mVar1;
long long mVar2;
int mVar3;
};
これは、mVar1、mVar2、mVar3 という名前の 3 つの変数からなる単純な構造体です。
mVar1 は int なので 4 バイトです。
mVar2 は long long int なので 8 バイトです。
mVar3 も int なので 4 バイトです。
ARM およびインテル® Atom™ プロセッサー・ベースのプラットフォーム上では、どれだけのメモリー空間が必要になるのでしょうか?
デフォルトのコンパイラー・オプションでコンパイルする場合、ARM およびインテル® Atom™ プロセッサー・ベースのプラットフォーム向けにコンパイルされるデータのサイズを図 10 に示します。ARM は自動的に double でアライメントするため 24 バイト必要になりますが、x86 では 16 バイトです。
図 10: デフォルトのコンパイラー・オプションを使用した場合のメモリー割り当て
ARM では mVar2 のような 64 ビット変数を 8 バイトでアライメントしなければならないため、TestStruct では 8 バイト (64 ビット) の mVar2 のレイアウトが異なります。多くの場合、x86 向けのビルドと ARM 向けのビルドでは全体のリビルドが必要になるため、これは問題にはならないでしょう。
ただし、アプリケーションがクラスや構造体をシリアル化するとサイズの不整合が発生します。例えば、ARM アプリケーションでファイルを作成し、そのファイルへ TestStruct を書き込むケースについて考えてみます。x86 プラットフォームでそのファイルからデータを読み込む場合、アプリケーションにおけるクラスサイズはファイルにおけるクラスサイズと異なります。同様のメモリー割り当ての問題は、特定のメモリーレイアウトを想定するネットワーク・トラフィックでも発生する可能性があります。
GCC* の “-malign-double” コンパイラー・オプションは、x86 と ARM の両方で同じメモリー割り当てを生成します。
図 11: -malign-double オプションを指定した場合のメモリー割り当て
4.2 NEON* 命令の SSE への移植[3]
4.2.1 NEON*
ARM NEON* テクノロジーは、主にスマートフォンや HDTV アプリケーションなどのマルチメディアで使用されています。ARM のドキュメントによると、ARM NEON* テクノロジーは ARM の 128 ビット SIMD エンジンベースのテクノロジー (ARM Cortex*–A シリーズの拡張) であり、ARMv5 アーキテクチャーと比較すると少なくとも 3 倍以上、その後継である ARMv6 アーキテクチャーとは 2 倍以上、パフォーマンスが向上しています。NEON* テクノロジーの詳細は、次の Web サイトを参照してください:
http://www.arm.com/products/processors/technologies/neon.php (英語)
4.2.2 インテル® SSE: 相当するインテルのテクノロジー
インテル® SSE とは、インテル® アーキテクチャー (IA) 向けのストリーミング SIMD 拡張命令です。インテル® Atom™ プロセッサーは現在、インテル® ストリーミング SIMD 拡張命令 3 補足命令 (インテル® SSSE3) に対応しており、インテル® SSE4.x にはまだ対応していません。インテル® SSE は、浮動小数点データのパック処理を行う 128 ビットのエンジンです。実行モデルはインテル® MMX® テクノロジーに端を発しており、SSEx は基本的にインテル® MMX® テクノロジーのより新しい世代です。詳細は、『Intel 64 and IA-32 Architectures Software Developer’s Manuals』 (英語) の「Volume 1: Basic Architecture」を参照してください。セクション 5.5 のインテル® SSE の概要に SSE、SSE2、SSE3、および SSSE3 命令があります。これらの命令は、XMM レジスター間、または XMM レジスターとメモリー間で指定された精度のパックド浮動小数点値を移動します。XMM レジスターが MMX レジスターの代わりに使用されます。
4.2.3 アセンブリー・レベルの NEON* からインテル® SSE への移植
『Intel 64 and IA-32 Architectures Software Developer’s Manuals』は、すべてのインテル® SSE(x) のクロスリファレンスとして使用できます。また、次の Web サイトに、インテル® SSE のアセンブリー・レベルの命令の説明があります:
http://neilkemp.us/src/sse_tutorial/sse_tutorial.html (英語)
目次からサンプルコードや詳細な背景情報をご覧になれます。
同様に、次の ARM のマニュアルから NEON* に関する情報が得られます。セクション 1.4 「NEON 向けの開発」に簡単なアセンブリーのサンプルがあります。
http://infocenter.arm.com/help/topic/com.arm.doc.dht0002a/DHT0002A_introducing_neon.pdf (英語)
NEON* とインテル® SSE のアセンブリー・コードの主な相違点:
- エンディアン: インテル® SSE はリトルエンディアンのアセンブリーのみをサポートしており、ARM はビッグエンディアンまたはリトルエンディアンをサポートしています (ARM はバイエンディアン)。提供されている ARM のコード例はリトルエンディアンで、インテルのコードと似ています。注: ARM コンパイラーではいくつかの留意事項があります。例えば、GCC* を使用して ARM 向けにコンパイルする場合は、–mlittle-endian および –mbig-endian コンパイラー・オプションを指定します。詳細は以下を参照してください:
http://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html (英語) - 粒度: 提供されている単純なアセンブリー・コードの例は、インテル® SSE の ADDPS 命令と、x = 8 や 16 などで NEON* の VADD.ix を比較しています。このことから、NEON* では処理するデータの粒度を指定していることが分かります。
注: これらの相違点がすべてではありません。NEON* とインテル® SSE の違いはこの他にも見られるでしょう。
4.2.4 C/C++ レベルの NEON* からインテル® SSE への移植
C/C++ コードや NEON* コードからインテル® SSE へ移植する場合、API に関してさまざまな留意事項があります。インライン・アセンブリーは使用されず、忠実な C/C++ コードが使用されることを前提としています。NEON* 命令はいくつかの C ライブラリーも提供します。これらの命令は C コードですが、インテル® Atom™ プロセッサー・ベースのプラットフォーム上で実行することはできないため、書き直す必要があります。
5. アプリケーション・パフォーマンスの最適化
5.1 パフォーマンス・チューニング
次の手法を使用してコーディング段階で、インテル® Atom™ プロセッサー・ベースのプラットフォーム上でアプリケーションのパフォーマンスを最適化できます。
5.1.1 よく使用されるショート関数の代わりにインライン関数を使用する
インライン関数は、プライベート・データ・メンバーへのアクセスのような小さな関数に最適です。ショート関数は、関数呼び出しのオーバーヘッドの影響を受けます。ロング関数は、その長さに比例して、呼び出し/リターンシーケンスにかかる時間が短くなり、インライン展開により得られる利点も減ります。[4]
インライン関数は次のオーバーヘッドを抑えることができます。
- 関数呼び出し (パラメーターの引き渡しおよびオブジェクト・アドレスのスタックへの配置を含む)
- 呼び出し元のスタックフレームの保持
- 新しいスタックフレームのセットアップ
- 戻り値の引き渡し
- 以前のスタックフレームの復元
- リターン
5.1.2 double の代わりに float を使用する
浮動小数点ユニット (FPU) は、浮動小数点数の演算 (加算、減算、乗算、除算、平方根など) を行うコンピューター・システムの処理装置です。一部のシステム (特に、古いマイクロコードベースのアーキテクチャー) では、指数関数や三角関数のような超越関数も実行できます。現在のプロセッサーは、ソフトウェア・ライブラリー・ルーチンを使用してこれらの計算を行います。最新の汎用コンピューター・アーキテクチャーでは、1 つ以上の FPU が CPU に統合されています[6]。
インテル® Atom™ プロセッサー・ベースのプラットフォームでは FPU を利用できます。ほとんどの場合、インテル® Atom™ プロセッサー・ベースのデバイスでは、double の代わりに float を使用することでデータの計算処理をスピードアップし、メモリー帯域幅を節約することができます。
5.1.3 マルチスレッド・コード
マルチスレッド・コードにより、インテル® Atom™ プロセッサーのハイパースレッド関数を使用して、スループットと全体のパフォーマンスを向上できます。マルチスレッドに関する詳細は、以下を参照してください:
5.2 コンパイラー・オプションによるハイパフォーマンスなアプリケーションのビルド
ご存知のように、Android* アプリケーションでは、ネイティブコードは GCC* でビルドされます。しかし、GCC* のデフォルトのターゲット・デバイスは、インテル® Pentium® Pro プロセッサーです。オプションを追加せずにネイティブコードをコンパイルすると、インテル® Pentium® Pro プロセッサー・ベースのプラットフォームに最適なバイナリーコードが生成されます。ほとんどの Android* アプリケーションは、インテル® Pentium® Pro プロセッサー・ベースではなく、インテル® Atom™ プロセッサー・ベースのプラットフォームで実行されるため、ターゲット・プラットフォームに応じて、特定のコンパイラー・オプションを指定することを強く推奨します。インテル® Atom™ プロセッサー・ベースのプラットフォームでコンパイルする場合は、次のコンパイラー・オプションを指定することを推奨します。
-march=atom
-msse4
-mavx
-maes
コンパイラー・オプションの引数に関する詳細は、以下を参照してください:
https://gcc.gnu.org/onlinedocs/gcc/i386-and-x86-64-Options.html (英語)
6. まとめ
ここでは、インテル® Atom™ プロセッサー・ベースのプラットフォームにおける Android* アプリケーションの開発と最適化、および NDK アプリケーションの開発と移植について述べました。
以下は重要なポイントの要約です。
- 多くの Android* アプリケーションは、インテル® Atom™ プロセッサー・ベースのプラットフォームでそのまま実行できます。NDK アプリケーションはネイティブコードを再コンパイルする必要があります。アプリケーションにアセンブリー・コードが含まれる場合は、その部分を書き直す必要があります。
- Android* アプリケーションのパフォーマンスを向上するには、IA (インテル® アーキテクチャー) の機能を最大限に活用します。
- GCC でより効率良いコードをビルドするには、プラットフォーム固有のコンパイラー・オプションを指定します。
参考文献
- http://software.intel.com/en-us/articles/installation-instructions-for-intel-hardware-accelerated-execution-manager-windows/ (英語)
- http://software.intel.com/en-us/blogs/2011/08/18/understanding-x86-vs-arm-memory-alignment-on-android/ (英語)
- http://software.intel.com/en-us/articles/ndk-android-application-porting-methodologies/ (英語)
- http://msdn.microsoft.com/en-us/library/1w2887zk.aspx (英語)
- https://developer.android.com/ndk/index.html (英語)
- http://en.wikipedia.org/wiki/Floating-point_unit (英語)
著者紹介
Dawei は x86 デバイス向け Android* アプリケーションの開発および最適化、Web HTML5 アプリケーション開発を含む、モバイル・アプリケーション・イネーブリング担当のアプリケーション・エンジニアです。モバイル・アプリケーションの UI と UX のデザインにおいても豊富な経験があります。
著作権と商標について
本資料に掲載されている情報は、インテル製品の概要説明を目的としたものです。本資料は、明示されているか否かにかかわらず、また禁反言によるとよらずにかかわらず、いかなる知的財産権のライセンスを許諾するものではありません。製品に付属の売買契約書『Intel’s Terms and Conditions of Sale』に規定されている場合を除き、インテルはいかなる責任を負うものではなく、またインテル製品の販売や使用に関する明示または黙示の保証 (特定目的への適合性、商品適格性、あらゆる特許権、著作権、その他知的財産権の非侵害性への保証を含む) に関してもいかなる責任も負いません。
インテルによる書面での合意がない限り、インテル製品は、その欠陥や故障によって人身事故が発生するようなアプリケーションでの使用を想定した設計は行われていません。
インテル製品は、予告なく仕様や説明が変更される場合があります。機能または命令の一覧で「留保」または「未定義」と記されているものがありますが、その「機能が存在しない」あるいは「性質が留保付である」という状態を設計の前提にしないでください。これらの項目は、インテルが将来のために留保しているものです。インテルが将来これらの項目を定義したことにより、衝突が生じたり互換性が失われたりしても、インテルは一切責任を負いません。この情報は予告なく変更されることがあります。この情報だけに基づいて設計を最終的なものとしないでください。
本資料で説明されている製品には、エラッタと呼ばれる設計上の不具合が含まれている可能性があり、公表されている仕様とは異なる動作をする場合があります。現在確認済みのエラッタについては、インテルまでお問い合わせください。
最新の仕様をご希望の場合や製品をご注文の場合は、お近くのインテルの営業所または販売代理店にお問い合わせください。
本資料で紹介されている資料番号付きのドキュメントや、インテルのその他の資料を入手するには、1-800-548-4725 (アメリカ合衆国) までご連絡いただくか、http://www.intel.com/design/literature.htm (英語) を参照してください。性能に関するテストに使用されるソフトウェアとワークロードは、性能がインテル® マイクロプロセッサー用に最適化されていることがあります。SYSmark* や MobileMark* などの性能テストは、特定のコンピューター・システム、コンポーネント、ソフトウェア、操作、機能に基づいて行ったものです。結果はこれらの要因によって異なります。製品の購入を検討される場合は、他の製品と組み合わせた場合の本製品の性能など、ほかの情報や性能テストも参考にして、パフォーマンスを総合的に評価することをお勧めします。
本資料に含まれるソフトウェア・ソース・コードはソフトウェア・ライセンス契約に基づいて提供されるものであり、その使用および複製はライセンス契約で定められた条件下でのみ許可されます。
Intel、インテル、Intel ロゴは、アメリカ合衆国および / またはその他の国における Intel Corporation の商標です。
© 2012 Intel Corporation. 無断での引用、転載を禁じます。
* その他の社名、製品名などは、一般に各社の表示、商標または登録商標です。
** このサンプルコードは、インテル・サンプル・ソース・コード使用許諾契約書 (英語) の下で公開されています。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。