日別アーカイブ: 2012年1月26日

Android 4.0 RenderScript と MultiCore CPU

以前 Tegra2 や Galaxy Nexus (OMAP4460) など Dual core CPU
搭載機種で RenderScript の動作テストを行いました。

 ・Android 3.x RenderScript (7) RenderScript Compute の速度
 ・Android 4.0 RenderScript Compute の速度 その2

予想に反してあまり速くなく、Single thread で書いた NDK コードと
ほとんど変わらない結果でした。
また Atomic 命令なしに競合するコードを記述しても不定な結果になりません。
そのため RenderScript は Single thread 実行しているのではないか
との結論でした。

その後 Eee Pad TF201 (Android 3.2) の Quad core CPU Tegra 3 で
走らせても同じで、Single Thread 相当の速度しか出ません。
使い方に問題があるかもしれないので、いろいろと試したところ
rsForEach() と forEach_root() で速度が違うことがわかりました。

API Level 14 (Android OS 4.0) 以降は RenderScript の interface が
変更されており、java から直接 cl の root を呼び出すことができます。

下記は API Level による HelloCompute のサンプルの違いです。

// RenderScript : API 11-13 (Android 3.x)

#pragma version(1)
#pragma rs java_package_name(com.example.android.rs.hellocompute)

rs_allocation gIn;
rs_allocation gOut;
rs_script gScript;

const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};

void root(const uchar4 *v_in, uchar4 *v_out, const void *usrData, uint32_t x, uint32_t y) {
    float4 f4 = rsUnpackColor8888(*v_in);

    float3 mono = dot(f4.rgb, gMonoMult);
    *v_out = rsPackColorTo8888(mono);
}

void filter() {
    rsForEach(gScript, gIn, gOut, 0);
}

Android 4.0 以降は filter() がありません。

// RenderScript : API 14-15 (Android 4.0.x)

#pragma version(1)
#pragma rs java_package_name(com.example.android.rs.hellocompute)

const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};

void root(const uchar4 *v_in, uchar4 *v_out) {
    float4 f4 = rsUnpackColor8888(*v_in);

    float3 mono = dot(f4.rgb, gMonoMult);
    *v_out = rsPackColorTo8888(mono);
}

Java から invoke_filter() 経由で script 内で rsForEach() を呼び出す
必要がなく、直接 forEach_root() を使って root() を呼べるからです。

// Java : API 11-13 (Android 3.x)

        mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono);

        mScript.set_gIn(mInAllocation);
        mScript.set_gOut(mOutAllocation);
        mScript.set_gScript(mScript);
        mScript.invoke_filter();
        mOutAllocation.copyTo(mBitmapOut);

Allocation の受け渡しもなく簡単になっています。

// Java : API 14-15 (Android 4.0.x)

        mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono);

        mScript.forEach_root(mInAllocation, mOutAllocation);
        mOutAllocation.copyTo(mBitmapOut);

Galaxy Nexus SC-04D (Android 4.0.1 API Level 14) でこの両者の
速度を比べると、API Level 14-15 の forEach_root() を用いた方が
およそ 2倍の速度で実行されます。
dual core による並列実行と考えられます。

更に調べると、rs 内の外部参照可能な関数に rsForEach() を使った
呼び出しを記述するだけで速度が遅くなることがわかりました。
つまり一番上の「// RenderScript : API 11-13 (Android 3.x)」を
forEach_root() で実行しても 1/2 の速度となります。
互換性のためなのか、条件によって thread 実行できない script と
みなされる何らかの理由があるのだと考えられます。

Android 3.x のデバイスでは forEach_root() は使えませんでした。
Tegra 3 など Quad core CPU の本当の能力が発揮されるのは
Android 4.0 へ アップデートしてからかもしれません。

続く RenderScript Tegra 3 の Quad core で Multi Thread 実行

関連エントリ
Android 3.x RenderScript (7) RenderScript Compute の速度
Android 4.0 RenderScript Compute の速度 その2