D3D Shader/OpenGL」カテゴリーアーカイブ

Android 4.0 MIPS で RenderScript, ainol Novo 7 Paladin の浮動小数点演算速度

Novo 7 Paladin は MIPS CPU で Android 4.0 を搭載した端末です。
RenderScript を試してみました。

前回の記事で触れたように、mips 対応版 NDK を入れると対応
アーキテクチャが一気に 6種類に増えます。
さらに armeabi-v7a や mips-r2 の NDK では、ハードウエアの性能を
引き出すために CPU のオプション機能を利用することができます。
ただし搭載していないハードもあるため事前に CPU Features を
チェックしなければなりません。
下記は NDK の cpu-features が返す値です。

armeabi-v7a:
 ANDROID_CPU_ARM_FEATURE_ARMv7
 ANDROID_CPU_ARM_FEATURE_VFPv3
 ANDROID_CPU_ARM_FEATURE_NEON

mips-r2:
 ANDROID_CPU_MIPS_FEATURE_R2
 ANDROID_CPU_MIPS_FEATURE_DSP
 ANDROID_CPU_MIPS_FEATURE_DSP_R2

種類が増えてくると個別最適化は困難となりますし、ビルドや
テストの手間もかかります。
こんな場合 CPU 非依存で Native 相当の実力を持つ RenderScript は
検討する価値がありそうです。

Android 4.0 対応、RenderScript のサンプル

上の記事に掲載したサンプルは MIPS の Paladin でも動作しました。
ただし一部修正が必要でした。
Android 3.x (API 11~13) をターゲットにビルドした RenderScript の
アプリケーションは実行時に下記のようなエラーが出ます。

Could not parse bitcode file
Failed to translate bitcode from version: 11

RenderScript の byte code (bitcode) は Android 3.x (API 11~13)
と Android 4.0 以上 (API 14 以降) で互換性が無いようです。
そのため API Level 14 以上を target にして再コンパイルする
必要があります。

(1) SdkVersion を 14 以上にする

AndroidManifest.xml の SdkVersion を 11 から 14 に修正します。

    

 ↓

    

もし同じフォルダに project.properties ファイルが作られているなら
target=android-15 (または 14) に書き換えます。

(2) rsForEach() を API 14 以降の書式に変更

上のページで紹介したプログラムでは必要ありませんが、Android
SDK 付属の RenderScript サンプルを走らせる場合は必要です。
API 14 以降は rsForEach() の引数が書式が変更されているので、
rsForEach() でコンパイルエラーが出たら引数最後の 0 を削除します。

(3) bc ファイルを作りなおす

bc ファイルを作りなおすためにプロジェクトを一旦 Clean してから
ビルドし直します。
Eclipse の場合はプロジェクトを選択して Menu Project → Clean…

これで Paladin (MIPS + Android 4.0) 上で RenderScript の
プログラムが動くようになります。
描画はそこそこですが CL 系は遅いです。
CPU が Single core であることに加え、浮動小数点演算もあまり
速くないようです。

●実行速度比較(1)

まずは Linpack for Android v1.2.8 で比較してみました

Single   Multi
MFLOPS  MFLOPS   OS   CPU
----------------------------------------------------------------------
18.091  --       2.3  Cortex-A8    1.0GHz S5PC110    Galaxy S SC-02B
18.592  --       4.0  MIPS XBurst  1.0GHz JZ4770     Novo7 Paladin
35.628  --       2.2  Scorpion     1.0GHz QSD8250    Desire X06HT
30.33   57.233   3.1  Cortex-A9 x2 1.0GHz Tegra2     Optimus Pad L-06C
46.164  74.664   2.3  Scorpion  x2 1.2GHz MSM8660    EVO 3D ISW12HT
57.342  92.981   2.3  Cortex-A9 x2 1.2GHz Exynos4210 Galaxy S2 SC-02C
47.071 140.908   3.2  Cortex-A9 x4 1.3GHz Tegra3     EeePad TF201

 * MFLOS の数値が大きい方が速い。
 * Single = Single-Thread
 * Multi = Multi-Thread

Multi Thread はタイミングによるばらつきが大きいため何度か
実行し最速の数値を取っています。あまり正確でないかもしれませんし、
単精度ではまた違った結果になると思います。
取り敢えず Paladin の fpu は激遅の Cortex-A8 VFP に近い速度
ということがわかりました。

●実行速度比較(2)

RenderScript, Java, NDK C, NDK asm の演算比較。
4×4 Matrix * Vector の 50万回。(1)~(13) すべて同じ演算。

                   Paladin     GS   Desire  Tegra2   GS2     GN   Tegra3
                    XBurst     A8  Scorpion  A9x2    A9x2   A9x2   A9x4
                    1.0GHz  1.0GHz  1.0GHz  1.0GHz  1.2GHz 1.2GHz 1.3GHz
------------------------------------------------------------------------
(1) RenderScript A     290      --     --      52      --     29     57
(2) RenderScript B     314      --     --      69      --     44     62
(3) RenderScript C     310      --     --      51      --     31     37
(4) Java Inline        482     698    418     244     179    174    167
(5) Java Matrix      11763    1012   1906    1232    1127   4785    840
(6) NDK (C func)       158     166     60      79      39     39     43
(7) NDK C inline1      267     288     49      66      43     33     41
(8) NDK C inline2      243     289     54      65      46     32     41
(9) NDK asm VFP         --     139     35      46      22     19     35
(10)NDK asm VFP MT2     --     139     37      26      14     15     48
(11)NDK C NEON Intrin   --      26     33      --      26     21     34
(12)NDK asm NEON        --      20     23      --      38     18     32
(13)NDK asm NEON MT2    --      22     29      --      26     17     17

実行時間(msec) 数値が小さいほうが速い。
  * GS=Galaxy S SC-02B,  GS2=Galaxy S2 SC-02C,  GN=Galaxy Nexus SC-04D
  * Tegra2= Optimus Pad L-06C, Tegra3= EeePad TF201
  * A8= Cortex-A8,  A9= Cortex-A9
  * Intrin= Intrinsic (Builtin Function)
  * MT2= Multi thread x2

速いデバイスほど実行時間が短く誤差が出ます。
また演算命令が高速になるほどメモリアクセス速度の影響が増えます。
マルチスレッド実行の場合スレッドの起動や同期によるばらつきが大きく
あまり正確ではありません。

RenderScript の演算では Paladin は A9 dual と 5~10倍くらいの
差が生じていることがわかります。

Paladin の fpu 自体は Cortex-A8 VFP より若干高速です。
ただし、Cortex-A8 も NEON を使うと数倍高速であり、A9 の速度に
ほとんど追い付いています。
Scorpion/A9 になると VFP/NEON 共に安定して速度が出ています。

注意点としてはJava の実行時間には GC が含まれている可能性があります。
また最高でも 2 Thread しか使っていないので Tegra3 は最速ケースでは
ありません。

関連エントリ
Android 4.0 ainol Novo 7 Paladin、MIPS CPU の NDK と Vivante GPU
Android 4.0 対応、RenderScript のサンプル
Android 3.x RenderScript (1)
Snapdragon の本当の浮動小数点演算能力
ARM Cortex-A8 の NEON と浮動小数演算最適化

Android 4.0 ainol Novo 7 Paladin、MIPS CPU の NDK と Vivante GPU

ARM でも x86 でもない、MIPS の CPU を搭載した
Android 端末 ainol Novo 7 Paladin を使ってみました。

Ingenic JZ4770
CPU core: XBurst (MIPS32)
GPU core: Vivante GC860

CPU/GPU (OpenGL ES Extension 等) は下記に追加

CPU/GPU: Novo7 Paladin Android 4.0 JZ4770 MIPS XBurst GC860

以下抜粋

processor		: MIPS-compatible processor JZ4770 
cpu model		: Ingenic Xburst 
ASEs implemented	: mxu
Features		: fpu mxu dsp lowpower 

GL_VERSION: OpenGL ES 2.0
GL_RENDERER: GC860 core
GL_VENDOR: Vivante Corporation
GL_SHADING_LANGUAGE_VERSION: OpenGL ES GLSL ES 1.00

下記の GPU 機能比較表にも追加しました

Mobile GPU の比較

GPU は Unified Shader で FragmentShader も highp 対応。
テクスチャ圧縮フォーマットは DXT1/3/5 (S3TC)、
24bit depth に対応しており depth_texture もあります。
Vertex の Stream 入力が若干少なめ。
レンダリングは 2048 までですが使えるテクスチャサイズが
8192 と突出しています。

NDK を使って実際にシェーダーを走らせてみました。
下記のベンチマーク結果の表に追加しています。

Mobile GPU bench mark

公式スペックもフィルレートがあまり高くなかったので、
GC800 の世代的には PVR SGX530/535 あたりと同等なのかもしれません。
より高速な GPU として GC1000/GC2000/GC4000 も存在しています。

Vivante Graphics Processor IP

Freescale i.MX5 系の GPU は AMD Z430 ですが、すでに Qualcomm
に売却されており Adreno と名称が変わっています。
i.MX6 では Vivante の新しい GPU core が採用されるようです。

●MIPS 版 NDK

Google の Android NDK には MIPS が含まれていませんが、
下記の MIPS サイトからダウンロードすることができます。

MIPS Developers: Download MIPS Android NDK

既存の NDK (ARM/x86) をすべて含んでいるため、同バージョンの
NDK と置き換えるだけで mips 対応になります。
例えばすでに C:/android/android-ndk-r7 というフォルダがあるなら

1. c:/android/android-ndk-r7 を削除
1. android-ndk-r7m-windows-20120103.zip を展開
2. android-ndk-r7m を android-ndk-r7 にリネームして C:/android に入れる

これだけです。ARM, x86, MIPS すべてに対応します。

MIPS 対応 NDK では新しく3つのアーキテクチャが追加されます。
倍になりました。既存のものと合わせてまとめると下記の通り。

Android NDK  IA         GCC                              fpu simd
---------------------------------------------------------------------------
armeabi      ARMv5TE    -march=armv5te                   --  --
armeabi-v7a  ARMv7A     -march=armv7-a -mfpu=vfp         VFP (NEON)
x86          IA-32      -march=i686 -mfpmath=sse -msse3  SSE SSE3
mips         MIPS32 R1  -mips32  -mhard-float            FPU --
mips-r2      MIPS32 R2  -mips32r2 -mhard-float           FPU (DSP ASE)
mips-r2-sf   MIPS32 R2  -mips32r2 -msoft-float           --  --

XBurst は ase もあり mips-r2 のように見えますが Paladin は mips でした。
(2012/02/14 追記: NDK では mips ですが ROM は mips-r2 で build されているそうです)
NDK の Application.mk 等に下記のように記述すれば全対応になります。

APP_ABI	:= armeabi armeabi-v7a x86 mips mips-r2 mips-r2-sf

バイナリ容量はかなり増えそうです。
RenderScript はまだ試していません。

(2012/02/14 追記: NDK r7 からは 「APP_ABI := all」と記述するだけで全対応になります。)

(2012/04/17 追記: NDK r7bm から mips-r2, mips-r2-sf が無くなりました)

RenderScript Tegra 3 の Quad core で Multi Thread 実行

RenderScript で Android 3.x でも Multi Thread 実行する方法が
わかりました。
以下その測定結果

         最小実行時間 速度比  OS       CPU
----------------------------------------------------------
Tegra 3        622ms   x2.6   A3.2.1   Cortex-A9 1.3GHz x4
OMAP4460      1334ms   x1.2   A4.0.1   Cortex-A9 1.2GHz x2
Tegra 2       1608ms   x1.0   A3.1     Cortex-A9 1.0GHz x2

 * 実行時間の数値が小さいほうが速い。
 * 何度か実行して最速時間を抽出。

(Eee Pad TF201, Galaxy Nexus SC-04D, Optimus Pad L-06C)

やっと RenderScript で Quad core による効果を確認できるように
なりました。さすがに Tegra 3 の CPU は速いです。

前の記事で書いたとおり Android 3.x の HelloCompute サンプルは
シングルスレッドで実行されます。

マルチスレッド実行できない条件は下記の通り

 RenderScript で自分自身のカーネルを呼び出す rsForEach() を記述する。

つまり下記のようなコードを用いて、rsForEach() が同じファイルの
root() 関数である (A) を呼び出すとだめです。

// RenderScript

void root( const uchar4 *v_in, uchar4 *v_out ) // -- (A)
{
  ~
}

rs_script  script;
uchar4*	   in_vec;
uchar4*	   out_vec;

void filter()
{
    rsForEach( script, rsGetAllocation( in_vec ), rsGetAllocation( out_vec ), 0 );
}

これを回避する方法は 2つあります。

●(1) Android 4.0 (API 14) 以降のみ

前回の記事に書いたとおり Java から直接 forEach_root() を使って
呼び出します。
Android 4.0 (API Level 14) 以降の HelloCompute はこれです。
同じ RenderScript 内の rsForEach() 呼び出しを通らないので実行が
スレッド並列化されます。

●(2) Android 3.0 (API 11) 以降対応

別の RenderScript から rsForEach() を使って呼び出します。

(2) の方法を使ったマルチスレッド実行できる RenderScript の例
(Android 3.2 Sample の HelloCompute を修正)

// RenderScript: mono.rs

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

// 実際に実行したい関数
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);
}

ダミーの RenderScript ファイルを作ります。call.rs

// RenderScript: call.rs

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

void root(const uchar4 *v_in, uchar4 *v_out)
{ // ダミー関数。呼ばれない。
}

rs_script script;
uchar4*	  in_vec;
uchar4*	  out_vec;

void callscript()
{
    rsForEach( script, rsGetAllocation( in_vec ), rsGetAllocation( out_vec ), 0 );
}

call.rs 経由で mono.rs を呼び出します。

// Java: HelloCompute.java

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

    // 追加
    mCall = new ScriptC_call(mRS, getResources(), R.raw.call);

    mCall.bind_in_vec( mInAllocation );
    mCall.bind_out_vec( mOutAllocation );
    mCall.set_script( mScript );

    // 削除
    //mScript.set_gIn(mInAllocation);
    //mScript.set_gOut(mOutAllocation);
    //mScript.set_gScript(mScript);
    //mScript.invoke_filter();

    // call.rs 経由で呼び出す
    mCall.invoke_callscript();

    mOutAllocation.copyTo(mBitmapOut);

●マルチスレッド実行の検出

mono.rs にわざと競合が発生するような修正を加えます。

// RenderScript: mono.rs

static uint32_t sum= 0;
static uint32_t sum2= 0;

void root(const uchar4 *v_in, uchar4 *v_out)
{
    uint32_t  a= sum;

    float4 f4 = rsUnpackColor8888(*v_in);
    float3 mono = dot(f4.rgb, gMonoMult);
    *v_out = rsPackColorTo8888(mono);

    sum= a+1;
    rsAtomicInc( &sum2 ); // Android 4.0 API 14 以降のみ
}

void dump_sum()
{
    rsDebug( "sum=", sum );
    rsDebug( "sum2=", sum2 );
}

実行後に Java から dump_sum() を呼び出します。

// Java: HelloCompute.java

    mCall.invoke_callscript();
    mOutAllocation.copyTo(mBitmapOut);

    mScript.invoke_dump_sum();  // 結果表示

期待通り sum が毎回不定な値となり、並列実行されている証拠が
取れました。

Android 4.0 以降では rsAtomic 命令が使えます。
上の例の場合 sum2 は常に一定となり、呼ばれた回数 == 画素数
を示すようになりました。

同一ファイル内の rsForEach() では、カーネルコード実行後に
static な変数にアクセスできます。
上の例のように、競合する集約されたコードが記述されている可能性があり
互換性の問題があったのかもしれません。

forEach のエントリは常に root() で、それぞれ個別のファイルに記述する
必要があります。C言語でも独立性が保たれます。
並列実行しやすいようこのような仕様になっているものと考えられます。

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

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

ASUS Eee Pad TF201 Tegra3 の GPU 速度

Transformer Prime, EeePad TF201 手に入れました。
NVIDIA の新しい Mobile プロセッサ Tegra 3 を搭載しています。
CPU は Cortex-A9 の quad core で GPU は ULP GeForce 12 core 版
となります。

CPU は 4 core 認識しています。Tegra2 と違い NEON あります。

Processor	: ARMv7 Processor rev 9 (v7l)
processor	: 0
BogoMIPS	: 1992.29

processor	: 1
BogoMIPS	: 1992.29

processor	: 2
BogoMIPS	: 1992.29

processor	: 3
BogoMIPS	: 1992.29

Features	: swp half thumb fastmult vfp edsp neon vfpv3 
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x2
CPU part	: 0xc09
CPU revision	: 9

GPU の方は Tegra2 と比べて特に機能的な違いがありませんでした。
depth は 16bit のままで depth_texture もありません。

Mobile GPU の機能比較

GL_VERSION: OpenGL ES 2.0 12.01014
GL_RENDERER: NVIDIA Tegra 3
GL_VENDOR: NVIDIA Corporation
GL_SHADING_LANGUAGE_VERSION: OpenGL ES GLSL 1.00

Extension は下記のページに追加しました。

OpenGL ES Extension

● GPU 速度

いつもの pixel 負荷の高い描画テストを走らせてみました。
Tegra2 と比べて 3倍弱といった数値になっています。

(2) light 3 (ambient + directional x2 + point )
-----------------------------------------------------
Tegra 3 / ULP GeForce(12)  15.70fps   EeePad TF201
Tegra 2 / ULP GeForce(8)    5.74fps   ICONIA TAB A500


(3) light 1 (ambient + directional )
-----------------------------------------------------
Tegra 3 / ULP GeForce(12)  30.10fps   EeePad TF201
Tegra 2 / ULP GeForce(8)   12.10fps   ICONIA TAB A500

shader core は Pixel が 2倍らしいので、Mali400 と同じ表現を
使えば MP2 相当です。2倍以上の性能差は動作クロックの違いだと
考えられます。

他の GPU との比較は下記のページにまとめました。

Mobile GPU bench mark

pix/sec の欄を見ると、速度的にはほぼ 3世代目グループである
Mali-400MP4/Adreno220/SGX543MP2 に並んでいることがわかります。

NVIDIA といえば GPU のイメージが強いですが Tegra はむしろ逆です。
優先しているのは CPU の方で、半世代先行した設計で Quad 化を
実現しています。
それに対して GPU は決して突出した性能ではなく、やっとライバルに
並んだところです。

Group3 | Adreno 220, PVR SGX543MP2, Mali-400MP4, ULP GeForce(12,Tegra3)
Group2 | Adreno 205, PVR SGX530/535/540, ULP GeForce(8,Tegra250)
Group1 | Adreno 200, AMD Z430

関連エントリ
OpenGL ES 2.0 Mobile GPU の比較、重いテクスチャ
頂点性能の比較 その2 (OpenGL ES 2.0 Mobile GPU)
OpenGL ES 2.0 Mobile GPU の頂点性能を比較する
A5 PowerVR SGX543MP2 は iOS 5 だと速い
さらに OpenGL ES 2.0 Mobile GPU の速度比較
OpenGL ES 2.0 Mobile GPU の速度比較 (dual core世代) 更新