OpenCL
OpenCL(Open Computing Language)は、マルチコアCPUやGPU などによる異種混在の計算資源において、
並列コンピューティングのためのクロスプラットフォームなAPIである。
標準化団体のクロノス・グループ(Khronos Group) によって仕様が策定され、
ロイヤリティフリーなオープン仕様として公開されている。
Android では、公式にはサポートされていないが。
CPU/GPUメーカーによりサポートされている。
機種依存性 高し。
Android で OpenCLの環境を作る
まず、CPU と GPUのメーカーと型番を調べる。
Nexus5 で実行した例
CPU
$ adb shell cat /proc/cpuinfo | grep Hardware
Hardware : Qualcomm MSM 8974 HAMMERHEAD (Flattened Device Tree)
GPU
$ adb shell dumpsys | grep GLES
GLES: Qualcomm, Adreno (TM) 330, OpenGL ES 3.0 V@127.0 AU@ (GIT@I96aee987eb)
これに対応したOpenCLドライバーを入手する。
Getting Started with OpenCL on Android
Qualcomm のサイトから OpenCLドライバーを入手すると、
ROM イメージを入れ替えるように記載されている。
[Qualcomm Tools & Resources]
(https://developer.qualcomm.com/software/adreno-gpu-sdk/tools)
もっと簡易な方法がないか調べてみると、
アプリに OpenCL ドライバーを同封する方法が見つかった。
githubリポジトリには、 Adreno 330 用のドライバーと、
ヘッダーファイルが同封されている。
今回は、これを試す。
下記にて OpenCL ドライバーが配布されているが、
動作せず。
github : Proprietary OpenCL drivers for Android
OpenCL ヘッダーファイルは
Khronos の github リポジトリで配布されている。
これに置き換えても動作する。
github : Khronos OpenCL-Headers
アプリを作成する
下記のプロジェクトファイルを自分の環境に合わせる。
下記を参考にした。
# OpenCL Header
set(OPENCL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../opencl)
set(LIB "${OPENCL_DIR}/lib")
include_directories(${OPENCL_DIR}/include)
# OpenCL Driver
add_library(libOpenCL SHARED IMPORTED )
set_target_properties(libOpenCL PROPERTIES
IMPORTED_LOCATION "${LIB}/${ANDROID_ABI}/libOpenCL.so")
# C++ コード をコンパイルする
add_library(
${target}
SHARED
native-lib.cpp )
# ターゲットをビルドする
target_link_libraries( # Specifies the target library.
${target}
libOpenCL
${log-lib} )
# "libOpenCL.so" をアプリに同封する
configure_file(
"${LIB}/${ANDROID_ABI}/libOpenCL.so"
"${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libOpenCL.so"
COPYONLY)
大きな修正が1つ必要です。
下記のようにカーネルファイルを読み込んでいる。
std::ifstream kernelFile(fileName, std::ios::in);
しかし、ネティブコードではアプリのファルダーにあるファイルは読み込めない。
Java にて、アプリのファルダーにあるファイルを読み込んで、
アプリごとのファイル領域にコピーして、
そのファイルパスをネティブコードに通知する方法にする。
protected void onResume() {
// カーネルファイルをコピ=してファイルパス名を取得する
String path = AssetFile.getFilePath(this, CL_FILE_NAME);
// ネティブコードを実行する
String text = stringFromJNI(path);
そのほかの修正。
元のプログラムでは、OpenCLのカーネルが正しく動作しているかを確認していない。
ホスト側 (CPU) と ターゲット側(GPU) で同じ計算を行い、照合するようにした。
// result1 : CPU の計算結果
// result2 : GPU の計算結果
for(int k = 0;k < ARRAY_SIZE;k++){
float f1 = result1[k];
float f2 = result2[k];
int r1 = (int)f1;
int r2 = (int)f2;
// LOGD("result: %f", f2);
// 照合する
if (r1 != r2) {
LOGD("unmstch: %f , %f", f1, f2);
return 1;
}
}
アプリを実行する
analyze apk により下記の2つが同封されていることが確認できる。
スクリーンショット
CPUとGPUの計算に要した時間を表示する
GPU の方が遅くなっている。
メモリ転送などのオーバーヘッドによるものと思われる。
サンプルコードをgithub に公開した。
https://github.com/ohwada/Android_Samples/tree/master/OpenCL1