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

OpenGL ES 2.0 Tegra2/3/4 の Fragment Shader と動的ループ

Desktop GPU で Vertex Shader と Pixel Shader の仕様が完全に
統一されたのは Direct3D 10 の ShaderModel 4.0 からです。
それまでは使える命令や Constant 領域、レジスタ数、プログラムサイズ、
テクスチャ命令など様々な違いが残っていました。

Hardware の Unified Shader 化は ATI の Xbox360 GPU が先行しています。
これが今の Qualcomm Adreno に繋がっていきます。
その後一般の Desktop PC 向けにも NVIDIA GeForce 8800 (G80) が登場し、
以後 Unified Shader model が当たり前となっています。

OpenGL ES 2.0 の Shader 世代は Direct3D 9 の Shader Model 3.0 に相当
するのですが、Mobile GPU の多くはすでに Unified Shader です。
そのためあまり Vertex と Fragment Shader の機能差を意識する必要が
ありませんでした。

そんな中、Tegra2/3/4 は G70 ベースの GPU であり、Unified Shader 化する前の
制限が残っている珍しいケースとなっています。

コメントで Tegra の Fragment Shader は動的ループが使えないとの
指摘を頂いたので試してみました。

// (1) 動的ループ
int loop= int( uniform_value );
for( int i= 0 ; i < loop ; i++ ){
   ...
}

↑ループ回数を動的に求めた場合、Vertex Shader は通りますが
Fragment Shader ではコンパイル時にエラーが発生します。

以前書いたように Uniform 配列の index アクセス ↓(2) も
Tegra の Fragment Shader ではエラーとなります。
どちらも Direct3D 9 世代の制限と考えられます。

// (2) Uniform 配列
uniform vec4 src_color[30];
~
int index= int(tex_color.x);
vec4 color= src_color[ index ]; // Tegra の Fragment Shader では Error

Tegra 4 でも同様なので、大幅に機能拡張されていても GPU のベースは
同じであることがわかります。
なお Tegra と同じように Discrete Type の Shader Unit を備える
Mai-400MP では動きました。

	     Vertex Shader            Fragment Shader
	     動的Loop  Uniform配列    動的Loop  Uniform配列
-----------------------------------------------------------
Unified      ◯         ◯              ◯         ◯
Mali-400MP   ◯         ◯              ◯         ◯
Tegra2/3/4   ◯         ◯              ERROR     ERROR

ただし Tegra でも動的分岐はできるので、実行時にループ回数が変動する
場合でも同等の処理を実現することは可能です。

// (3) Tegra向け 動的分岐によるループ
for( int i= 0 ; i< 100 ; i++ ){
    if( i >= Loop ){
        break;
    }
    ~ 動的ループ相当
}

以下まとめ (いずれも Fragment Shader の場合)

コンパイル時にループ回数が定まらない場合はエラー。

// (4) ループ回数不定  -- Tegra で ERROR
for( int i= start ; i< end ; i++ ){
    ~
}

ループの範囲が決まっていればコンパイル可能。

// (5) 上限が決まってる場合 -- Tegra でも OK
for( int i= 0 ; i< 100 ; i++ ){
    if( i >= start && i < end ){
        ~ 動的ループ相当
    }
}

おそらく下記のように展開されているものと考えられます。

i= 0;
if( i >= start && i < end ){
    ~
}
i= 1;
if( i >= start && i < end ){
    ~
}

~

i= 99;
if( i >= start && i < end ){
    ~
}

Tegra K1 以降は ShaderModel 5.0 世代の GPU core になるので
このようなハードウエア的な制限はなくなるでしょう。

GPU 互換性など気軽に相談してください。

関連エントリ
OpenGL ES 2.0 Adreno 330, Tegra 4 の GPU 速度
OpenGL ES 2.0 で迷路シェーダー
GLSL互換性

Android SmartWatch ZWatch で 3Dゲーム (ChiRaKS)

昔作成した 3D ポリゴンのゲームを移植してみました。
CPU だけでレンダリングしているので、3D GPU が入っていない Z Watch でも動きます。

zwatch_chiraks01.png

ChiRaKS (Android)

当時 (2000年) は SH3 30~60MHz の SHARP MI-Zaurus (PDA) で動いており
160×160 サイズのレンダリングでぎりぎり 30fps に届かない程度でした。

その後移植した PocketPC (WindowsCE/WindowsMobile) では、すでに iPAQ に
StrongARM (206MHz) が搭載されており 100fps も余裕で超えています。
同世代の MIPS VR4122 端末 (150MHz) は StrongARM の 1/3 くらいの速度でした。

現在のハイエンド端末は single core でも Zaurus の 100倍以上速いので
1080×1080 サイズのレンダリングでも 60fps 近く出ます。
ただし 32bit 固定小数点演算を行っている関係上、演算がオーバーフローしてしまう
問題があったため、アプリでは最大解像度を 540×540 に制限しています。

Z Watch (JZ4775 MIPS) の画面は 240×240 と小さいこともあり余裕です。
設定の NoWait にチェックを入れると 60fps 出ていることがわかります。

レンダリングには NDK の Bitmap API を利用しています。

#include  
~
void Render( JNIEnv* env, jobject obj, jobject bitmap_object )
{
    void* ptr= NULL;
    AndroidBitmap_lockPixels( env, bitmap_object, &ptr );
    ~
    AndroidBitmap_unlockPixels( env, bitmap_object );
}

関連エントリ
Android SmartWatch スマートウオッチのスペック比較表
Android SmartWatch SmartQ ZWatch (4) アプリの管理
Android SmartWatch SmartQ ZWatch (3) 腕に関数電卓
Android SmartWatch SmartQ ZWatch (2)
Android 4.1 SmartWatch SmartQ Z Watch

MediaTek MT8125/8389 Cortex-A7 の浮動小数点演算速度

ARM Cortex-A7 は big.LITTLE で用いられる場合、省電力側の CPU core に相当します。
消費電力が少ない代わりに、高性能側の CPU core より性能は劣ります。
VFP Benchmark の結果を送ってもらいました。

結果を見ると、Cortex-A7 の NEON は 32bit 単位で実行していることがわかります。
演算速度は NEON 無しの CPU と変わらないのですが、Cortex-A15 のペアとして
機能できるよう NEON 命令セットに対応しているものと考えられます。

SIMD (Vector)         SIMD4 single fp (32bit x4)
CPU                   mul    add     mad     fma
------------------------------------------------------
ARM Cortex-A7         1      1       2       2
ARM Cortex-A8         2      2       4       –
ARM Cortex-A9         2      2       4       –
ARM Cortex-A15        4      4       8       8
Qualcomm Scorpion     4      4       8       –
Qualcomm Krait 400    4      4       8       8
Apple A6 Swift        4      4       8       8
Apple A7 Cyclone 32   8      12      16      16
Apple A7 Cyclone 64   8      12      –       16

  * 数値は 1 cycle あたりの演算数、大きい方が速い

FPU の演算能力 (上記以外の結果はこちら)

big.LITTLE で用いる場合は、この手の演算は Cortex-A15 の担当なので
おそらく表に出ることは無いでしょう。
単独で用いられている場合は、同じ Quad core CPU と書かれていても
かなり性能差が開いていることを考慮した方が良いかもしれません。
浮動小数点演算速度だけ見てもピーク演算速度で Cortex-A9 の半分、
Krait/Cortex-A15 の 1/4 (同一クロック時) となっています。

以下詳細

Lenovo YOGA TABLET 8 (3G)
MediaTek MT8389 1.2GHz Cortex-A7 Quad core

SingleT SP max: 2.374 GFLOPS
SingleT DP max: 1.165 GFLOPS
MultiT  SP max: 9.474 GFLOPS
MultiT  DP max: 4.653 GFLOPS

* VFP/NEON (single fp)
VFP fmuls (32bit x1) n8       :    3.634     1100.7     1100.7
VFP fadds (32bit x1) n8       :    3.450     1159.3     1159.3
VFP fmacs (32bit x1) n8       :    3.451     2318.1     2318.1
VFP vfma.f32 (32bit x1) n8    :    3.448     2319.9     2319.9
NEON vmul.f32 (32bit x2) n8   :    6.795     1177.3     1177.3
NEON vadd.f32 (32bit x2) n8   :    6.828     1171.7     1171.7
NEON vmla.f32 (32bit x2) n8   :    6.810     2349.6     2349.6
NEON vfma.f32 (32bit x2) n8   :    6.797     2354.1     2354.1
NEON vmul.f32 (32bit x4) n8   :   13.529     1182.7     1182.7
NEON vadd.f32 (32bit x4) n8   :   13.511     1184.2     1184.2
NEON vmla.f32 (32bit x4) n8   :   13.498     2370.7     2370.7
NEON vfma.f32 (32bit x4) n8   :   13.549     2361.8     2361.8

倍精度の場合はさらに差が広がっており、加算は 1 cycle で実行できますが乗算は 4 倍低速です。
さらに fmacd (積和) は乗算と同等の速度で演算しているものの、
vfma (FMA) は並列化されておらず 5倍 (1 add + 4 mul cycle) かかっているようです。

* VFP/NEON (double fp)
VFP fmuld (64bit x1) n8       :   13.628      293.5      293.5
VFP faddd (64bit x1) n8       :    3.439     1163.0     1163.0
VFP fmacd (64bit x1) n8       :   13.508      592.2      592.2
VFP vfma.f64 (64bit x1) n8    :   16.895      473.5      473.5
VFP fmuld (64bit x1) ns4      :   13.434      297.8      297.8
VFP faddd (64bit x1) ns4      :    3.435     1164.6     1164.6
VFP fmacd (64bit x1) ns4      :   13.430      595.7      595.7
VFP vfma.f64 (64bit x1) ns4   :   16.823      475.5      475.5
VFP fmuld (64bit x1) n1       :   13.439      297.6      297.6
VFP faddd (64bit x1) n1       :    3.447     1160.6     1160.6
VFP fmacd (64bit x1) n1       :   26.856      297.9      297.9
VFP vfma.f64 (64bit x1) n1    :   26.860      297.8      297.8

関連エントリ
VFP Benchmark v1.1 浮動小数点演算命令の速度 (NEON/SSE/AVX)
ARM CPU の VFP Benchmark アプリ 浮動小数点演算速度の計測
iPhone 5s A7 CPU の浮動小数点演算速度 (2) (arm64/AArch64/64bit)
iPhone 5s A7 CPU の浮動小数点演算速度 (32bit)
Nexus 10 CPU Cortex-A15 の速度
Nexus 10 CPU Cortex-A15 の浮動小数点演算速度
Qualcomm APQ8064 GPU Adreno 320 の速度
benchmark 関連

Linux 他 X11 で OpenGL 4 を使う

X11 (X window) では Platform Interface に GLX を用います。
Display が本当に意味を持つのは X11 ならではで、egl の API 仕様も
X window のプログラムを見ると納得です。

Display* display= NULL;
int      screen= 0;

display= XOpenDisplay( NULL );
screen= DefaultScreen( display );

(1) pixel format の選択

static int attrib_list[]= {
    GLX_RENDER_TYPE,    GLX_RGBA_BIT,
    GLX_DRAWABLE_TYPE,  GLX_WINDOW_BIT,
    GLX_DOUBLEBUFFER,   True,
    GLX_RED_SIZE,       8,
    GLX_GREEN_SIZE,     8,
    GLX_BLUE_SIZE,      8,
    GLX_ALPHA_SIZE,     8,
    GLX_DEPTH_SIZE,     24,
    GLX_STENCIL_SIZE,   8,
    None
};
int  n_items= 0;
GLXFBConfig*  config_array= glXChooseFBConfig( display, screen, attrib_list, &n_items );
〜
if( n_items ){
    GLXFBConfig fb_config= config_array[0];
    〜
}
XFree( config_array );

サーバーが対応しているピクセルフォーマットから必要なものを選択しています。
フォーマットの組み合わせは GPU やドライバによって異なります。

X native の API にも似たような仕組みとして XVisualInfo があります。
GLXFBConfig は GLX による拡張で、XVisualInfo* の代わりに用いることが可能です。

Windows でも Win32 API に PIXELFORMATDESCRIPTOR (ChoosePixelFormat())
がありました。WGL Extension の WGL_ARB_pixel_format
(wglChoosePixelFormatARB()) はこれを置き換えるもので、
XVisualInfo と GLXFBConfig との関係に似ています。

もし XCreateColormap などで Visual (XVisualInfo) が必要になったら
下記のように取得すれば OK です。

// GLXFBConfig -> XVisualInfo
XVisualInfo* visual_info= glXGetVisualFromFBConfig( display, fb_config );
〜
XFree( visual_info );

上の visual_info は config_array 同様 XFree() が必要です。
egl のような wrapper を作る場合、メモリ管理を伴うのは少々都合が悪い
場合があります。
GLXFBConfig をそのまま保持せずに、ID に紐付けておくと管理が楽になります。

// GLXFBConfig -> fb_config_id
int  fb_config_id= 0;
glXGetFBConfigAttrib( display, fb_config, GLX_FBCONFIG_ID, &fb_config_id );

この fb_config_id があれば GLXConfig が一意に定まるわけです。
fb_config_id から GLXFBConfig を取り出す方法は下記の通り。

// fb_config_id -> GLXFBConfig
int attr_list[]= { GLX_FBCONFIG_ID, fb_config_id, None };
int n_items= 0;
GLXFBConfig* fb_config_ptr= glXChooseFBConfig( display, screen, attr_list, &n_items );
assert( n_items == 1 );
GLXFBConfig fb_config= fb_config_ptr[0];
〜
XFree( fb_config_ptr );

XVisualInfo を得る方法はすでに述べた通りですが、GLXFBConfig から
直接 VisualID を求めることもできます。

// GLXFBConfig -> VisualID
int  visual_id= 0;
glXGetFBConfigAttrib( display, config_array[0], GLX_VISUAL_ID, &visual_id );

(2) Context の作成

任意の Version の OpenGL Context を作成するには GLX_ARB_create_context
( glXCreateContextAttribsARB() ) が必要です。
試した下記の環境ではデフォルトで OpenGL 4.3 が選択されていたので、
場合によっては使わなくても問題ないかもしれません。

・Ubuntu 13.10 desktop x86_64
・GeForce GTX650
・NVIDIA binary Xorg driver 319

Core Profile のみ、OpenGL ES 2.0 互換 Profile の指定など、細かい設定を
行うならやはり glXCreateContextAttribsARB() が必要です。

// Extension API glXCreateContextAttribsARB() の取り出し
PFNGLXCREATECONTEXTATTRIBSARBPROC f_glXCreateContextAttribsARB=
  (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress(
       (const GLubyte*)"glXCreateContextAttribsARB" );

// OpenGL 4.3 context 作成
static int  att_command[]= {
    GLX_CONTEXT_MAJOR_VERSION_ARB,  4,
    GLX_CONTEXT_MINOR_VERSION_ARB,  3,
    GLX_CONTEXT_FLAGS_ARB,          0,
    GLX_CONTEXT_PROFILE_MASK_ARB,   GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
    None
};
GLXContext  context= f_glXCreateContextAttribsARB( display, fb_config, 0, True, att_command );

他にも glX extension を利用する場合が考えられるので、
API の取得はまとめて管理した方が良いでしょう。
glX extension だけでなく、GL 4.x の各命令でも API の取得が必要になります。

4番目の True は Direct Rendering の指定です。
本来の X protocol はネットワーク透過なのでアプリケーションからドライバに
直接アクセスできませんが、ローカルサーバー利用時は条件によって可能になります。
現在の context が Direct Rendering 可能な状態 (DRI) かどうかは
glXIsDirect() で確認することができます。

GL_ARB_create_context

(3) Window の作成

GLXFBConfig が決まった時点で Window を作れるので (2) との順番は任意。

XVisualInfo* visual_info= glXGetVisualFromFBConfig( display, fb_config );
Window  root= RootWindow( NativeDisplay, screen );
XSetWindowAttributes    attr;
attr.background_pixel= 0;
attr.border_pixel= 0;
attr.colormap= XCreateColormap( display, root, visual_info->visual, AllocNone );
attr.event_mask= 0
    |StructureNotifyMask
    |ExposureMask
    |KeyPressMask
    |KeyReleaseMask
    |ButtonPressMask
    |ButtonReleaseMask
    ;
Window	win= XCreateWindow( display, root, x, y, width, height,
    0,	// border
    visual_info->depth, InputOutput, visual_info->visual,
    CWBorderPixel|CWColormap|CWEventMask, &attr );
XFree( visual_info );
visual_info= NULL;

XMapWindow( display, win );
XFlush( display );

(4) MakeCurrent

glXMakeCurrent( display, win, context );

Window は GLXDrawable です。
これでレンダリングできますが、OpenGL 2 以降の API を用いるには
個別に glXGetProcAddress() して関数を用意する必要があります。

(5) OpenGL API の取得

ゼロから始めるとここが一番手間かもしれません。
ただしこの流れは Windows で OpenGL を用いる場合と完全に同一です。
API 名 Table の作成、API の取り出しコードは Windows と共有することができます。

OpenGL のはじめ方 (Windows)

下記は API Table の例です。
OpenGL 4.x の glcorearb.h からスクリプトで自動変換して生成しています。
この方法だと、新しい API や extension 対応ドライバがリリースされたら
即座に対応することができます。

// flatlib3 GL_HeaderParser.py
// linux/GLFunction_GL4.inc

void (APIENTRY *p_glBlendColor)( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha );
void (APIENTRY *p_glBlendEquation)( GLenum mode );
void (APIENTRY *p_glDrawRangeElements)( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices );
〜
void (APIENTRY *p_glTexPageCommitmentARB)( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident );
// total=525


ImportListType ImportList_GL4[]= {
{ "glBlendColor", (void*)&p_glBlendColor },
{ "glBlendEquation", (void*)&p_glBlendEquation },
{ "glDrawRangeElements", (void*)&p_glDrawRangeElements },
{ "glTexImage3D", (void*)&p_glTexImage3D },
{ "glTexSubImage3D", (void*)&p_glTexSubImage3D },
〜
{ "glGetNamedStringARB", (void*)&p_glGetNamedStringARB },
{ "glGetNamedStringivARB", (void*)&p_glGetNamedStringivARB },
{ "glTexPageCommitmentARB", (void*)&p_glTexPageCommitmentARB },
{ NULL, NULL }

};
// total=525
// flatlib3 GL_HeaderParser.py
// linux/GLFunction_GL4.h

extern void (APIENTRY *p_glBlendColor)( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha );
inline void glBlendColor( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha )
{
    FL_GLAPI_ASSERT( p_glBlendColor );
    return p_glBlendColor( red, green, blue, alpha );
}
extern void (APIENTRY *p_glBlendEquation)( GLenum mode );
inline void glBlendEquation( GLenum mode )
{
    FL_GLAPI_ASSERT( p_glBlendEquation );
    return p_glBlendEquation( mode );
}
extern void (APIENTRY *p_glDrawRangeElements)( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices );
inline void glDrawRangeElements( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices )
{
    FL_GLAPI_ASSERT( p_glDrawRangeElements );
    return p_glDrawRangeElements( mode, start, end, count, type, indices );
}
〜

Platform Interface のまとめ

Windows         WGL         GL4.x   wglGetProcAddress()
Linux X11       GLX         GL4.x   glXGetProcAddress()
OSX             NSOpenGL    GL4.1
OSX + X11       GLX         GL2.1   (XQuartz + HD4000)
Linux X11 + ES  EGL         ES2/3
Android         EGL         ES2/3
iOS             GLK/EAGL    ES2/3

Windows/Linux X11 は GetProcAddress() が必要ですが対応ドライバがあれば
新しい API をすぐ使えます。
OSX はそのまま OpenGL を使えて手間いらずですが 10.9 は OpenGL 4.1 まで。
OSX + XQuartz (HD4000) は残念ながら 2.1 でした。

Nexus 7 (2012) + Ubuntu desktop のように OpenGL ES 2.0 対応 GPU の
Linux では GLX ではなく X11 上で EGL + OpenGL ES 2.0 API が使えます。

Raspberry Pi や 旧 NetWalker のように、X が動いても OpenGL 未対応の場合
フレームバッファに直接レンダリングすることになります。
この場合も EGL なので、Window の有無にかかわらず利用できる EGL は互換性が
取りやすくなっています。

関連エントリ
Mac OS X 10.9 Mavericks の OpenGL 4.1 対応と Windows 8.1 Intel HD 4000 Driver
Mac OS X で OpenGL の描画 (Xcode5/retina)
Nexus 7 (2012) Ubuntu desktop 上での開発とバッテリー設定
NetWalker PC-Z1 i.MX515 OpenGL ES 2.0 (3)
OpenGL のはじめ方

VFP Benchmark v1.1 浮動小数点演算命令の速度 (NEON/SSE/AVX)

x86 の SSE/AVX 命令に対応しました。
ARM CPU と同じように SSE/AVX の命令速度を計測することができます。

VFP Benchmark v1.1
VFP Benchmark Android (Google play)
VFP Benchmark iOS (AppStore)

上記は Android ですが iOS 版では ARMv8A (arm64) に対応しています。(2014/02/05 iOS 版追加)
下記は手持ちのデバイスでの結果。

Device           CPU                             sp GFLOPS  dp GFLOPS
---------------------------------------------------------------------
MacBookRetina13  Core i5-3210M Ivy    2.5GHz x2       90.2       45.2
Kindle HDX 7     MSN8974  Krait 400   2.2GHz x4       67.5       16.9
Tegra Note 7     Tegra4   Cortex-A15  1.8GHz x4       51.3        9.8
Nexus 7 (2013)   AQP8064  Krait       1.5GHz x4       47.8       11.8
iPhone 5s        A7 arm64 Cyclone     1.3GHz x2       40.9       20.5
iPhone 5s        A7 arm7s Cyclone     1.3GHz x2       40.9        8.0
Mac mini 2009    Core2 Duo P7350      2.0GHz x2       31.7       12.7
Nexus 10         Exynos5D Cortex-A15  1.7GHz x2       26.7        5.3
iPad 4           A6X      Swift       1.4GHz x2       21.5        3.6
iPhone 5         A6       Swift       1.3GHz x2       20.1        3.4
Nexus 7 (2012)   Tegra3   Cortex-A9   1.3GHz x4       18.9        4.7
EVO 3D ISW12HT   MSM8660  Scorpion    1.2GHz x2       16.6        1.3
VAIO Type P      Atom Z540 Bonnell    1.8GHz x1       10.9        1.9
Desire X06HT     QSD8250  Scorpion    1.0GHz x1        7.1        0.9
iPad 2           A5       Cortex-A9   1.0GHz x2        7.8        2.0
iPod touch 4     A4       Cortex-A8   0.8GHz x1        3.1        0.1
Raspberry Pi     BCM2835  ARM1176JZFS 0.7GHz x1        0.7        0.7

 * 数値が大きい方が速い。
 * sp= 単精度, dp= 倍精度

ピーク値の測定なので実アプリケーションの速度とは異なります。
詳しくはこちらを参照してください。
下記のページのほぼ理論値通りの傾向が出ています。

CPU FLOPS 理論値

倍精度のテストはまだ改良の余地があります。
スカラーで mul+add のペアリングを測定していないので、
一部の CPU でもう少しスコアが伸びると考えられます。

Core i5 Ivy Bridge は想定より高い数値が出ていますが、TurboBoost の効果で
より高いクロックで走っているようです。single-thread 時は 3.0GHz、
mult-thread 時は 2.85GHz 相当の結果となっています。

実際の測定結果は命令単位の数値を出すので、より細かく CPU の動作を
調べることができます。

SSE2/AVX1 には積和命令がありませんが、Intel CPU は加算と乗算命令が
並列に実行できるようです。
↓ 実際に addps/mulps の Interleave は半分の時間で実行しています。

Ivy Bridge Core i5-3210M
* SSE/AVX (single fp)                sec     MFLOPS     MFLOPS
AVX vmulps (32bit x8) n8      :    1.322    24205.7    24205.7
AVX vaddps (32bit x8) n8      :    1.319    24256.0    24256.0
AVX vmul+addps (32bit x8) n8  :    0.658    48604.4    48604.4

↓ Atom (Bonnell) は場合少々特殊です。
SSE 命令の乗算は加算よりも 2倍時間がかかっています。
動作クロックを考えると SSE の add が 128bit で mul が 64bit 幅で
演算していると考えられます。

Atom Z540 (Bonnell)
* SSE/AVX (single fp)                sec     MFLOPS     MFLOPS
SSE mulps (32bit x4) n8       :    4.307     3715.2     3715.2
SSE addps (32bit x4) n8       :    2.207     7248.1     7248.1
SSE mul+addps (32bit x4) n8   :    2.155     7424.2     7424.2

ARM NEON の場合は、同じ SIMD でも 64bit 命令があります。
例えば「 vadd.f32 d0,d1,d2 」は単精度 32bit x2 の 64bit 加算を行うので、
Cortex-A8/A9 のように 64bit 幅でも 1cycle で実行します。
128bit 命令「 vadd.f32 q0,q1,q1 」の場合は 2cycle かかります。

SSE は常に 4要素 = 128bit 単位なので Pentium 3 等 64bit 幅の
SIMD Unit では最小でも 2cycle かかることになります。
同様に Atom の乗算も最小値は 2cycle です。
ただし mulps + addps の Interleave でも、addps のみの場合と同じ時間で
完了するため、加算と乗算は非対称ながら Overlap できるようです。

Atom には HT があるので、Multi-thread 時は無駄な隙間を埋められます。
メインスレッドで mulps + addps のペアを実行し、サブスレッドで addps のみ
走らせるとおそらく 128bit + 64bit の非対称なパイプラインが埋まります。

mulps + addps + addps 組み合わせを 2スレッド走らせたのが下記の結果で、
スコアが伸びていることがわかります。

Atom Z540 (Bonnell)
* SSE/AVX (single fp) multi-thread   sec     MFLOPS     MFLOPS
SSE ml+ad+addps (32bit x4) n6 :    3.075    10926.6    10926.6

これらの測定結果から、CPU の個別の演算能力をまとめたのが下記の表です。
倍精度の値はもう少し変動する可能性があります。

・スカラー

                  単精度                      倍精度
CPU               mul    add    mad   fma     mul    add    mad    fma
-----------------------------------------    -------------------------
ARM1176JZF-S      0.5    0.5      1    --     0.5    0.5      1     --
Cortex-A8        0.14   0.14   0.18    --     0.1    0.1    0.1     --
Cortex-A9           1      1      2    --     0.5      1      1     --
Cortex-A15          1      1    1.4     2       1      1    1.4    1.4
Scorpion            1      1      2    --     0.5      1      1     --
Krait 400           1      1      2     2       1      1    1.6      2
A6 Swift            1      1      1     1       1      1      1      1
A7 Cyclone arm7s    1      1      2     2       2      3      3      3
A7 Cyclone arm64    2      3     --     4       2      3     --    1.6
Atom Bonnell        1      1     --    --     0.5      1     --     --
Core2 Penryn        1      1     --    --       1      1     --     --
Core i5 Ivy Bridge  1      1     --    --       1      1     --     --

 * 数値は 1 cycle に実行可能な演算数
 * 値が大きい方が高速

ARM11 の mul は 0.5演算/cycle となっています。
つまり単精度の加算や乗算は 2cycle かかります。

mad/fma は命令あたり 2 演算なので、この欄が 2 の場合に 1 cycle で
実行できることを意味しています。

Cortex-A8 のピーク FLOPS は NEON のおかげで ARM11 より高いですが、
上記のように VFP のスカラー演算では ARM11 に負けています。

A7 Cyclone (ARMv8A) は AArch32 (32bit mode) と AArch64 (64bit mode) で
かなり違いがあります。
単精度演算は 64bit mode の方が数倍速く実行できるようです。
おそらく VFP が要求する仕様が NEON と異なっているためだと考えられます。
AArch64 は NEON と統一されたので、NEON と同等の速度で動作できるようです。
VFP が足を引っ張る似たような傾向は、Cortex-A15 など他の ARMv7A CPU にも
見られます。

・SIMD 単精度

                   SIMD2 (32bit x 2)         SIMD4 (32bit x4)
CPU                mul   add   mad   fma     mul   add   mad   fma  
----------------------------------------    ----------------------
ARM1176JZF-S        --    --    --    --      --    --    --    --
Cortex-A8            2     2     4    --       2     2     4    --
Cortex-A9            2     2     4    --       2     2     4    --
Cortex-A15           4     4     8     8       4     4     8     8 
Scorpion             2     2     4    --       4     4     8    -- 
Krait 400            2     2     4     4       4     4     8     8 
A6 Swift             2     2     4     4       4     4     8     8 
A7 Cyclone arm7s     4     6     8     8       8    12    16    16 
A7 Cyclone arm64     4     6    --     8       8    12    --    16
Atom Bonnell        --    --    --    --       2     4    (6)   --
Core2 Penryn        --    --    --    --       4     4    (8)   --
Core i5 Ivy Bridge  --    --    --    --       4     4    (8)   --

Cortex-A8/A9 は 64bit 幅なので、SIMD2 では Scorpion/Krait/Swift と
差がありません。
SIMD4 では 128bit の Scorpion/Krait/Swift の半分であることがわかります。

ユニークなのは Cortex-A15 で、SIMD4 では同じ 128bit の
Scorpion/Krait/Swift と同等ですが SIMD2 では 2倍の数値となっています。
Cortex-A15 は 64bit 幅 2 pipe なので、2命令同時実行できるためです。
スカラーでは単精度でも 1命令/cycle だったので、半分しか使わなくても
NEON の方が速いことになります。

Ivy Bridge は AVX に対応しているので、上の表では省略していますが SIMD8
があります。下記のページに SIMD8 や倍精度 SIMD 含めて表にまとめています。

CPU FLOPS : FPU

一番最初の GFLOPS のリストでは、Quad core かつ動作クロックの高い
Snapdragon 800 (MSM8974) が上位でした。
CPU の cycle 単位の命令数を出してみると、唯一の ARMv8 CPU でもある
A7 Cyclone が群を抜いて高性能であることがわかります。

計測結果から、mul, mad/fma で 2命令、add では 3命令を同時に実行できるようです。
NEON の場合は AArch32 と AArch64 特に違いはありませんでした。

A7 Cyclone の設計は DEC Alpha や StrongARM に由来するエンジニアが
関わっているとのこと。(Wikipedia P.A.Semi)

Benchmark はあくまで浮動小数点演算能力のピーク値を実測することが目的なので、
必ずしも総合的な優劣には一致しないことを予めご了承ください。

関連エントリ
ARM CPU の VFP Benchmark アプリ 浮動小数点演算速度の計測
iPhone 5s A7 CPU の浮動小数点演算速度 (2) (arm64/AArch64/64bit)
iPhone 5s A7 CPU の浮動小数点演算速度 (32bit)
Nexus 10 CPU Cortex-A15 の速度
Nexus 10 CPU Cortex-A15 の浮動小数点演算速度
Qualcomm APQ8064 GPU Adreno 320 の速度
Qualcomm APQ8064 Krait/A6 swift の浮動小数点演算能力
ARM Cortex-A8 の NEON と浮動小数演算最適化
benchmark 関連