Archives

February 2013 の記事

Android 向けに新しい Chrome がリリースされ、WebGL 対応になったようです。

「膨大な変更」でパフォーマンス向上した「Chrome for Android」最新版

WebGL を有効にする方法 (手順は PC版 Chrome と同じです)

(1) URL に "about:flags" と入力 (または "chrome://flags")
(2) リスト内の "WebGL を有効にする" を有効にする
(3) ブラウザの再起動

有効になっているかどうか下記の手順で確認できます。
(機能を有効にしても GPU によっては使えない可能性があります。)

(1) URL 欄に "chrome://gpu-internals" と入力
(2013/04/07 追記: 新しい Chrome は gpu-internals ではなく "chrome://gpu" に変更されています)

chrome_webgl.png

Android には様々なブラウザアプリケーションが揃っているので、
これまでも WebGL を使うことができました。

Opera , Firefox はすでに Android 版で WebGL に対応しており
3D 描画ができるようになっています。

Chrome の正式版はこれまで未対応でしたが、
先行配信されていた Chrome Beta ではすでに機能が入っていました。
WebGL の次は NativeClient へと、徐々に対応機能が広がっていくものと考えられます。


2013/02/24
PlayStation 4

PC では 4 core x HT でハードウエア 8 スレッドは当たり前になりましたが、
完全に独立した 8 core でプログラムを走らせる機会はまだあまり多くありません。
1 core あたりの演算能力がどの程度かわかりませんが、
うまく並列化できれば最大 8倍 (数倍)のパフォーマンスを見積もれるのは
ユニークだと思います。

ゲーム機は Xbox360 や PS3 の時点で multi core + HT (Hardware Multi Threading)
が当たり前となっています。
ただし core の数と高い動作クロック数を維持するために、
これまでは非常にシンプルなデザインの CPU core が用いられていました。

PPE+SPE で 8 core 搭載する Cell はもちろん、3 core の Xenon も
pipeline は in-order で、単体の実行効率はあまり高くありません。
その代わり 2 thread を interleave することで見かけのレイテンシを減らしています。
3.2GHz の 1個は、ある意味 1.6GHz 2個相当といえるのかもしれません。

比較的単純な In-order の core に HT を組み合わせて
高いクロックで走らせる手法は Atom に近い考え方です。
ただし Atom は省電力に用い、ゲーム機は高性能化のためで目的が異なります。
また Atom は interleave ではなく、さらに core あたりの演算能力は
Cell PPE, Xenon の方が高くなっています。

PS4 の CPU はこれまでと違い、比較的実行効率の高い core が多数
並列に存在していることになります。
ハイエンド core ではありませんが out-of-order の 2並列なので、
おそらく Cell/Xenon/Atom よりも、
クロック周波数に対して違和感ない速度で動作するのではないかと予想されます。

Cell/Xenon/Atom は HT 依存 In-order であり
ピーク性能は高くても single thread では性能を引き出すことができないため、
クロック周波数よりも遅い印象を受けるからです。
速いけどピーキーな CPU が多数存在していた前世代から、
PC と同じように比較的扱いやすい CPU core で均等な 8並列に移行したのだと
考えられます。


GPU 性能に関してもバス速度で推測出来ます。
176GB/sec の帯域があるそうなので、少なくてもこれくらい必要とするだけの能力が
システム全体に備わっていると言えます。

GC         2.6GB/s
PS2        3.2GB/s (+VRAM 48GB/s)
Xbox1      6.4GB/s
Xbox360   22.4GB/s (+ED RAM 32GB/s)
PS3       22.4GB/s (+CPU 25.6GB/s)    ( 22.4 =  3.2  x7.0倍)
PS4      176  GB/s                    (176.0 = 22.4  x7.8倍)

PCハイエンドの GPU では VRAM が 200GB/s を優に超えているのですが、
VRAM 容量では single で 8GB 搭載したビデオカードは自分が知る限りでは
まだ無かったように思います。
今までできなかったメモリをふんだんに使用したアルゴリズムが
利用できるようになりそうです。

おそらく CPU 性能や演算能力ではモバイルデバイスの進化の方が速いので、
数年後に追いつかれる可能性があります。
ですが GPU とバス帯域に関しては、据え置き専用機としてのアドバンテージが
十分備わっているといえるのではないでしょうか。


ユーザーとして欲しいかどうかは置いといて、
開発者としてはどのようなアルゴリズムを実装してみようかと、
いろいろと考えるだけでも楽しみなハードウエアではないかと思います。
モバイルデバイスの方が進化が早いと思っていましたが少々興味を持ちました。


関連エントリ
2007/01/22: PS3 PPU は速いのか


Nexus 7 の MultiROM が Ubuntu touch 対応となっています。

xda: [WiFi&3G] MultiROM v8 (new recovery for Ubuntu Touch)

TWRP の新しいバージョン (2013/02/22以降) で

xda: Ubuntu touch preview

この手順に従い install 可能となりました。
Android, Ubuntu Desktop, Ubuntu Touch を共存させて
起動時に選択することができます。

これでタブレットの利用には Android を、
単体でのプログラミングなら Ubuntu Desktop を、
Ubuntu Touch UI やアプリのテスト時に Desktop Preview を起動できます。

Ubuntu Touch Developer preview は adb 経由で chroot することからもわかるように、
Android 上に install した chroot 版 Linux に近い構造となっているようです。
HW アクセラレート対応の UI が乗っており、
Android SDK の AVD のようにアプリケーションの動作確認に利用することができます。

Ubuntu をベースとした Tablet/Phone 向け OS なので、
Android のようにタッチによる操作がしやすくなっています。
ただし Touch UI 上で Linux アプリがそのまま使えるわけではないので、
新しいプラットフォームと考えた方が良いでしょう。
UI 上で Linux らしさを期待するなら、今のところは Desktop 版との使い分けが
ベストだと思います。


関連エントリ
Nexus 7 Ubuntu Touch Developer Preview
Nexus 7 上に開発環境をつくる (4) Ubuntu 13.04
Nexus 7 上に開発環境をつくる (3) Ubuntu
Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)


Nexus 7 等のモバイルデバイスで動く Ubuntu Touch を試してみました。
開発者向けです。

Ubuntu Touch

Nexus 7 には下記のように通常の desktop 版 Ubuntu もあります。

Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)

↓この二つは別物です。

◎ Nexus 7 で動く Desktop 版 Ubuntu
◎ Nexus 7 で動く Ubuntu Touch Developer Preview

Desktop 版は PC と同一の豊富なアプリケーションを動かすことができる反面、
タッチだけの操作は必ずしも使いやすいとはいえませんでした。
キーボードやマウスを繋げば快適で、超小型のパソコンになります。

Ubuntu Touch は Android や iOS のように、タッチ操作に特化した UI が入ります。
まだ開発者向けで、操作に HOST PC が必要となることがあります。(2013/02/22現在)


● install

Desktop 版 Ubuntu の native install と同じように、本体の Android OS を置き換える形になります。
(Android のデータは全部消えます。)
MultiROM が対応すれば、他の環境と共存できるようになるかもしれません。

Install

Ubuntu 上で走る installer が用意されています。
PC に Ubuntu が入っている状態なら上記の Install 手順に従うだけです。

コマンドは python で書かれているので、
手順がわかれば Windows から手動で入れることもできるかもしれません。

bootloader の unlock が必要です。
installer は fastboot だけでなく adb も使うので、
Android を起動し USB デバッグにチェックを入れておきます。


● UI

インストールが完了すると、端末上で Ubuntu Touch の UI を試すことが可能となります。

ReleaseNotes

UI 上でできることがまだ少ないですが、ReleaseNotes に
書かれているように USB + adb 経由でシェルにログインできます。

adb root
adb shell

端末上で shell が動いたらさらにコマンドを実行します。

ubuntu_chroot shell

これで ARM 版 Ubuntu 12.10 としてシェルが動きます。
ReleaseNotes にあるように ssh を入れておけば Wi-Fi 経由で login できます。

apt-get update 
apt-get install openssh-server 

普通の Linux コマンドを install できるようです。コンパイラとか動きます。
残念ながら ubuntu-sdk は入れられませんでした。


関連エントリ
Nexus 7 上に開発環境をつくる (4) Ubuntu 13.04
Nexus 7 上に開発環境をつくる (3) Ubuntu
Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)


先週の Nexus 7 Ubuntu 13.04 image で、
新規インストールができない問題が出ていたようです。
古いバージョンなら大丈夫で、手元では 2013/02/05 の image が正しく動いています。

xda: [WiFi&3G] MultiROM v8 - (4.2.2 kernel and 4.18 bootloader fix)

また MultiROM 環境の Ubuntu で、apt-get upgrade / dist-upgrade と
フル更新をかけると OS の起動に失敗するようになります。

こちらは TWRP の Recovery menu モードに入ってから
Advanced → MultiROM → List ROMs で 選択 → re-patch init
で修復できるとのことです。

>Re-patch init - this is available only for ubuntu. Use it when ubuntu
> cannot find root partition, ie. after apt-get upgrade which changed
> the init script.


Android 4.2.2 への更新が配信されていましたが、
自動で更新できなかったので手動で行いました。

Gadget 好きの忘備録: Nexus7を4.2.2にUpdate

こちらによれば Factory Image を書き込めば良いとのことです。
boot と recovery はどうせ置き換えるので、system だけ書き込んでみました。
アーカイブ内の bat ファイルでは全パーティションを erase していましたが
そのまま実行しています。あくまで自己責任でお願いします。
また MultiROM 対応の kernel も 4.2.2 用に置き換えています。


関連エントリ
Nexus 7 上に開発環境をつくる (3) Ubuntu
Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)


スレッド間の同期を取る方法は複数あります。
OS のカーネルオブジェクト、ライブラリ関数、OS 依存の API など。
他にも CAS のような最低限の atomic 操作を使って Spinlock を作ることができます。
もちろんハードウエアスレッド環境に限ります。

C++11 対応に伴いコンパイラも新しい atomic 命令に移行しています。
例えば gcc の builtin 命令は __sync_ から __atomic_ となり、
clang にも同様に __c11_atomic_ があります。

Windows の場合は OS に Interlocked~ API がありますが、
VC++ が生成する同名の Intrinsic バージョンもあります。
こちらも C++11 に従いメモリバリアを指定できる命令が追加されているようです。

// CAS
gcc      __atomic_compare_exchange_*()
clang    __c11_atomic_compare_exchange_*()
Windows  _InterlockedCompareExchange_*()

gcc 4.7.2 : Built-in functions for memory model aware atomic operations
Clang 3.3 : Clang Language Extensions
msdn : x86 Intrinsics List


↓各種 API による比較です。

(1) Windows             Win8 x86   Win8 x64
-------------------------------------------------------
SpinLock(intrinsic)         5852       6353
CriticalSection            20894      22610
Win32 Mutex              2537901    3790403


(2) Linux              Linux x86   Linux x64   Linux ARM
-------------------------------------------------------
SpinLock(gcc atomic)      12712       10641       27977
pthread mutex             13693       11796       23807


(3) MacOS X         MacOS X x86  MacOS X x64
-------------------------------------------------------
SpinLock(OSAtomic)         9505        9070
pthread mutex            563013      467785

// 実行時間 単位=us

3つのスレッドで同じカウンタをそれぞれ 50000回カウントしています。
100% 競合する状態で、ロック手段による違いを測定しています。

走らせた PC のスペックが異なることと、数値の変動が大きいため
単純な比較ができないのでご注意ください。
同じ OS 内で API による違いだけ見てください。


(1) Windows (Windows 8 + VS2012 Express)

カーネルオブジェクトである Mutex は非常に遅くなっています。
CriticalSection の方が2桁高速ですが、_Interlocked 命令を使った
SpinLock の方が更に速い結果となっています。


(2) Linux (Ubuntu 12.10 / 13.04)

x86/x64 は VMware Player によるテストなのでご了承ください。
特筆すべき点は pthread_mutex が SpinLock と全く変わらないか、
むしろ速いということです。

SpinLock を作る場合に atomic API の使い方を間違えると、
pthread_mutex よりも遅くなったり、ロックに失敗したりするようです。

X86/x64 の場合 gcc (4.7.2) builtin の __atomic_ でメモリアクセス方法に
__ATOMIC_SEQ_CST を指定すると pthread_mutex よりも低速でした。
__ATOMIC_RELAXED で pthread と同等になります。
また Legacy API ですが __sync_ を使った場合も pthread と同等の
速度となりました。

ARM (Cortex-A9) では RELAXED の場合メモリアクセスが競合し
正しくブロックされません。
上記テストは ARM の場合のみ SEQ_CST を指定しています。
また __sync_ も RELAXED 相当なためかロックされませんでした。


(3) MacOS X (10.8)

OSAtomic と phtread_mutex で大きな隔たりがあります。
同じ pthread の mutex でも、Linux と違い Windows のように
カーネルオブジェクトとして実装されている可能性があります。


API が同一でも OS と CPU によって挙動が違うことがわかりました。
Linux では pthread_mutex が最もよい選択ですが、
MacOS X では逆に pthread_mutex を避けた方が良いようです。


テストプログラムは下記のとおり。

template<typename LockT>
class LockTest {
    LockT  lock;
    static int Counter;
    enum {
        MAX_LOOP= 50000
    };
public:
    void thread_func()
    {
        for( int i= 0 ; i< MAX_LOOP ; i++ ){
            lock.Lock();
            Counter++;
            lock.Unlock();
        }
    }
    void Run()
    {
        ClockTimer time;

        Counter= 0;
        Thread t0, t1, t2;

        t0.Run( this, &LockTest<LockT>::thread_func );
        t1.Run( this, &LockTest<LockT>::thread_func );
        t2.Run( this, &LockTest<LockT>::thread_func );

        t0.Join();
        t1.Join();
        t2.Join();

        time.Output();
        assert( Counter == MAX_LOOP * 3 );
    }
};


関連?エントリ
C++11 Rvalue Reference 右辺値参照


Khronos Conformant Products に OpenGL ES 3.0 の名前が
登場しています。

Intel HD Graphics 4000/2500
PowerVR Rouge Hood (PowerVR Series 6)
Snapdragon MSM8974 (Adreno 330)
Snapdragon MSM8064 (Adreno 320)

OpenGLES 3.0 API は GL 4.3 の互換モードや Emulator ですでにテスト可能(詳細)
ですが、モバイルデバイスでも HW の対応が進行しています。
ARM Mali はもちろん Vivante もすでに 3.0 に対応しているとのことです。

Vivante

↑の i.MX6 Quad といえば thanko Android SmartTV Quad-core に使われています。

唯一対応が未定で 2.0 のままなのは Tegra シリーズだけとなっています。
NVIDIA には Desktop GPU があるため余裕があるのかもしれません。


関連エントリ
OpenGL 4.3/GLES 3.0 次の圧縮テクスチャ ASTC
OpenGL ES 3.0 と OpenGL ES 2.0 の互換性
OpenGL ES 3.0 と Shader Model の関係、まとめ wiki の更新
OpenGL ES 2.0/3.0 Emulator
OpenGL 4.3 と GL_ARB_ES3_compatibility


Nexus 7 の Ubuntu 13.04 は armhf (hard-float) です。
Android NDK は softfp なので、
Ubuntu の方が関数呼び出しが効率化されていると考えられます。
比べてみました。

// 元のソース
float func2( float a, float b, float c )
{
    return  a * b + c;
}

float func3( float a, float b, float c )
{
    return  a + b - c;
}

float func1( float a, float b )
{
    return  a * b + a;
}

↓gcc によるコンパイル結果 (softfp)

// -marm -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3

    fmsr    s14, r0
    fmsr    s13, r2
    fmsr    s15, r1
    fmacs   s13, s14, s15
    fmrs    r0, s13
    bx  lr

    fmsr    s13, r0
    fmsr    s14, r1
    fadds   s15, s13, s14
    fmsr    s13, r2
    fsubs   s13, s15, s13
    fmrs    r0, s13
    bx  lr

    fmsr    s13, r0
    fmsr    s15, r1
    fmacs   s13, s13, s15
    fmrs    r0, s13
    bx  lr

soft といっても浮動小数点演算をエミュレーション実行しているわけではなく、
上記のように VFP や NEON 等の HW 演算ユニットが使われています。

あくまで ABI (Calling Convention) の話で、関数の呼び出し時のレジスタの
使われ方が異なります。
softfp の場合は FPU (VFP) が無い場合 (soft) と互換性が取れるように、
レジスタ渡しの場合に浮動小数点値も整数レジスタ(r)に入ります。

上の結果でも、毎回整数レジスタ(r)に入った引数を VFP レジスタ(s)へ
コピーしていることがわかります。

-mfloat-abi=hard を指定すると↓下記のように不要な転送が無くなりました。
引数や戻り値としてそのまま VFP レジスタ(s)が使われています。

// -marm -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3

    fmacs   s2, s0, s1
    fcpys   s0, s2
    bx  lr

    fadds   s0, s0, s1
    fsubs   s0, s0, s2
    bx  lr

    fmacs   s0, s0, s1
    bx  lr

↓さらに vfpv4 を指定すると、fmacs の代わりに vfma が使われていることがわかります。

// -marm -march=armv7-a -mfloat-abi=hard -mfpu=vfpv4

    vfma.f32    s2, s0, s1
    fcpys   s0, s2
    bx  lr

    fadds   s0, s0, s1
    fsubs   s0, s0, s2
    bx  lr

    vfma.f32    s0, s0, s1
    bx  lr

NEON 命令も試してみました。

// 元のソース
#include   <arm_neon.h>

float32x4_t func4( float32x4_t a, float32x4_t b )
{
    return  a * b + a;
}

-ffast-math を付けると neon 命令に変換できます。

↓softfp では 128bit x2 の値をレジスタだけで渡すことができません。
2つ目の引数が stack に入っています。

// -marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon -ffast-math

	vmov	d16, r0, r1  @ v4sf
	vmov	d17, r2, r3
	vld1.64	{d18-d19}, [sp:64]
	vmla.f32	q8, q9, q8
	vmov	r0, r1, d16  @ v4sf
	vmov	r2, r3, d17
	bx	lr

↓hard の場合すべてレジスタで受け渡し可能となります。

// -marm -march=armv7-a -mfloat-abi=hard -mfpu=neon -ffast-math

	vmla.f32	q0, q1, q0
	bx	lr

以上より hard-float の場合に下記の 2つのメリットあるようです。
(他にもあるかもしれません)

・整数レジスタとの転送が不要となる
・引数として利用可能なレジスタの個数が増える

具体的な速度は測定していませんが、より高速に実行できると考えられます。

Android NDK だけでなく iOS の新しいアーキテクチャ armv7s でも softfp
相当となっているようです。


関連エントリ
Nexus 7 上に開発環境をつくる (3) Ubuntu
Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)


Nexus 7 に Ubuntu 13.04を入れています。
Bluetooth が使えるようになりかなり実用度が増しました。
下記はあくまで 2013/02/14 現在のものです。
日々更新されていますので、そのまま鵜呑みにせずその時点での
最新情報を探すようにしてください。


●オンラインストレージ

NetWalker の時は ARM で動く Dropbox が無く不便だったのですが、
今では Ubuntu One が入っているので全く困らなくなりました。
同様に使えるオンラインストレージで、もちろん ARM で動きます。
5GB まで無料。


● WiMAX / 有線LAN

WiMAX ルーターに Wi-Fi でつながるのは当たり前ですが、
USB による有線接続も可能でした。試したのは下記の 2機種。

・NEC Aterm WM3600R
・NEC Aterm WM3800R

USB Host にケーブルでつなぐだけで認識します。
駅前など Wi-Fi が混雑していて安定しない場所で使えるかもしれません。
有線 LAN アダプタ LUA3-U2-ATX も使えています。


●テザリング

同様に SB iPhone5, au HTC J butterfly HTL21 も USB 接続しましたが
こちらはつながりません。
もちろん Wi-Fi や Bluetooth ではテザリングできます。

Bluetooth の場合はペアリングだけではだめで、
「Use your mobile phone as a network device (PAN/NAP)」
のチェックが必要です。
その後端末側から接続を選ぶ→右上の Network のポップアップから
端末名を選択する流れになります。


●スクリーンキーボード

リサイズ出来ます。
横画面で大きめにしておくとタッチしやすくなります。

マウスポインタアイコンで拡張パネルが開き、右ボタン等の操作が可能ですが、
複雑な操作をしていると固まる事が多いです。
マウスは使えるので、タッチが反応しない場合や突然ロック画面に飛ばされた
場合は、マウスをつないで再起動した方が良いです。


●armhf

こちらのコメントにも書きましたが hard-float なので、
NDK より呼び出し効率が上がっているものと考えられます。
続きます。


関連エントリ
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)
Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0


C++11 の新機能を使うとオブジェクトの無駄なコピーを減らすことができます。

記述が簡単になるなど、C++11 にはいろいろ便利に使える追加機能があります。
Rvalue Reference の場合はむしろコードが増えるのですが、
うまく使うことでプログラムの動作を効率化することができます。
とりあえず適当に文字列型を作ってみます。

class MyString {
    char*   Name;
private:
    void Clear()
    {
        delete[] Name;
        Name= NULL;
    }
    void Copy( const char* str )
    {
        Clear();
        size_t  length= strlen(str)+1;
        Name= new char[length];
        memcpy( Name, str, sizeof(char)*length );
    }
    void DeepCopy( const MyString& src )
    {
        Copy( src.Name );
    }
public:
    MyString() : Name( NULL ) {}
    MyString( const MyString& src ) : Name( NULL )
    {
        DeepCopy( src );
    }
    MyString( const char* str ) : Name( NULL )
    {
        Copy( str );
    }
    ~MyString()
    {
        Clear();
    }
    MyString& operator=( const MyString& src )
    {
        DeepCopy( src );
        return  *this;
    }
};

copy 時に文字列バッファの複製が発生します。
例えばこんな感じ。

MyString  str1;
MyString  str2;
str1= "abcdefg";              // -- (1)
str2= MyString( "ABCDEF" );   // -- (2)
str1= str2;                   // -- (3)

C++11 の Rvalue Reference を使うと特定のケースで copy を move に変換出来ます。
対応コードを追加したのが下記の形。

#include    <string.h>
#include    <utility>

#ifndef IS_CPP11
# define    IS_CPP11    (__cplusplus > 199711L)
#endif

int  AllocCount;
int  FreeCount;

class MyString {
    char*   Name;
private:
    void Clear()
    {
        if( Name ){
            FreeCount++;
        }
        delete[] Name;
        Name= NULL;
    }
    void Copy( const char* str )
    {
        Clear();
        size_t  length= strlen(str)+1;
        Name= new char[length];
        memcpy( Name, str, sizeof(char)*length );
        AllocCount++;
    }
    void DeepCopy( const MyString& src )
    {
        Copy( src.Name );
    }
public:
    MyString() : Name( NULL ) {}
    MyString( const MyString& src ) : Name( NULL )
    {
        DeepCopy( src );
    }
    MyString( const char* str ) : Name( NULL )
    {
        Copy( str );
    }
    ~MyString()
    {
        Clear();
    }
    MyString& operator=( const MyString& src )
    {
        DeepCopy( src );
        return  *this;
    }
    //-- ↓ここから追加分
#if IS_CPP11
    MyString( MyString&& src )
    {
        Name= src.Name;
        src.Name= NULL;
    }
    MyString& operator=( MyString&& src )
    {
        char*   tmp= Name;
        Name= src.Name;
        src.Name= tmp;
        return  *this;
    }
#endif
};

これで転送元を捨てても構わない場合に copy ではなく破壊転送 (move) が行われます。
上の場合はメモリを確保し直す必要がないので効率が上がります。

転送元を捨てても構わないケースの代表が、
一時確保されたオブジェクトである rvalue 右辺値です。
また明示的に std::move() をつければ任意の値を破壊転送することができます。

これで C++11 の場合は (1),(2) において copy が消えます。
どちらも一時的な object が作られているからです。

さらに適当なベクターを作ってみます。

template<typename T>
class MyVector {
    T*      Buffer;
    T*      Ptr;
    size_t  BufferSize;
private:
    void Clear()
    {
        delete[] Buffer;
        Buffer= Ptr= NULL;
    }
public:
    MyVector() : Buffer( NULL ), Ptr( NULL ), BufferSize( 0 ) {};
    MyVector( size_t size ) : BufferSize( size )
    {
        Ptr= Buffer= new T[size];
    }
    ~MyVector()
    {
        Clear();
    }
    size_t Size() const
    {
        return  Ptr - Buffer;
    }
    T& operator[]( size_t index )
    {
        return  Buffer[index];
    }
    const T& operator[]( size_t index ) const
    {
        return  Buffer[index];
    }
    void PushBack( const T& src )
    {
        assert( Ptr < Buffer + BufferSize );
        *Ptr++= src;
    }
#if IS_CPP11
    void PushBack( T&& src )
    {
        assert( Ptr < Buffer + BufferSize );
        *Ptr++= std::forward<T>(src);
    }
#endif
};

ループさせてどの程度高速化されるか測ってみます。

// main.cpp
int main()
{
    AllocCount= 0;
    FreeCount= 0;
    {
        const int   LOOP_MAX= 100000;
        MyVector<MyString>  string_list( LOOP_MAX );

        for( int i= 0 ; i< LOOP_MAX ; i++ ){
            string_list.PushBack( "12345" );
        }
    }
    printf( "alloc=%d  free=%d\n", AllocCount, FreeCount );
    return  0;
}

差が出るように作ったので C++11 でコンパイルした方が速いのは当然なのですが、
実際にメモリ確保の回数が半減していることがわかります。

Prog  time         output
c03   0m0.015s     alloc=200000  free=200000
c11   0m0.009s     alloc=100000  free=100000


# Makefile
mac:
	clang++ -std=c++11 -stdlib=libc++  -O4 main.cpp  -o c11
	clang++ -std=c++03 -stdlib=libc++  -O4 main.cpp  -o c03

linux:
	g++ -std=c++11   -O4 main.cpp  -o g11
	g++ -std=c++03   -O4 main.cpp  -o g03
	clang++ -std=c++11 -O3 main.cpp  -o c11
	clang++ -std=c++03 -O3 main.cpp  -o c03

win:
	cl /O2 main.cpp /Fev11.exe -DIS_CPP11=1
	cl /O2 main.cpp /Fev03.exe -DIS_CPP11=0

特定のケースで明らかな無駄を省いているだけなので、通常はここまで差がでません。
例えば (3) の場合何もせずに copy を減らすことはできません。

move の場合は結局 move 用に別の API を設けていることになります。
またオブジェクトだけでなく、copy が発生する API を経由する場合も
copy の他に move 用の別のルートを追加する必要があります。
途中で move() や forward() を付け忘れると、オブジェクトまで到達しないで
途切れてしまいます。

上の MyVector 自体も複製できるようにしてみます。

template<typename T>
class MyVector {
    T*      Buffer;
    T*      Ptr;
    size_t  BufferSize;
private:
    void Clear()
    {
        delete[] Buffer;
        Buffer= Ptr= NULL;
    }
#if IS_CPP11
    void Move( MyVector&& src )
    {
        Clear();
        BufferSize= src.BufferSize;
        Ptr= Buffer= new T[BufferSize];
        for( int i= 0 ; i< BufferSize ; i++ ){
            *Ptr++= std::move(src[i]);  //-- (4)
        }
    }
#endif
    void DeepCopy( const MyVector& src )
    {
        Clear();
        BufferSize= src.BufferSize;
        Ptr= Buffer= new T[BufferSize];
        for( int i= 0 ; i< BufferSize ; i++ ){
            *Ptr++= src[i];
        }
    }
public:
    MyVector() : Buffer( NULL ), Ptr( NULL ), BufferSize( 0 ) {};
    MyVector( size_t size ) : BufferSize( size )
    {
        Ptr= Buffer= new T[size];
    }
    ~MyVector()
    {
        Clear();
    }
    size_t Size() const
    {
        return  Ptr - Buffer;
    }
    void PushBack( const T& src )
    {
        assert( Ptr < Buffer + BufferSize );
        *Ptr++= src;
    }
#if IS_CPP11
    void PushBack( T&& src )
    {
        assert( Ptr < Buffer + BufferSize );
        *Ptr++= std::forward<T>(src);
    }
#endif
    T& operator[]( size_t index )
    {
        return  Buffer[index];
    }
    const T& operator[]( size_t index ) const
    {
        return  Buffer[index];
    }


    MyVector& operator=( const MyVector& src )
    {
        DeepCopy( src );
        return  *this;
    }
#if IS_CPP11
    MyVector& operator=( MyVector&& src )
    {
        Move( std::move(src) );
        return  *this;
    }
#endif
};

下記のように MyVector を代入できるようになりました。
この (5) のケースでは完全な move となり string のメモリ確保が発生しません。

int main()
{
    AllocCount= 0;
    FreeCount= 0;
    {
        const int   LOOP_MAX= 100000;
        MyVector<MyString>  string_list( LOOP_MAX );

        for( int i= 0 ; i< LOOP_MAX ; i++ ){
            string_list.PushBack( "12345" );
        }

        MyVector<MyString>  string_list2( LOOP_MAX );
        string_list2= std::move(string_list);    // -- (5)
    }
    printf( "alloc=%d  free=%d\n", AllocCount, FreeCount );
    return  0;
}

ですが std::move() を付け忘れると move でなく MyString の copy になります。
下記のように alloc/free が増えます。(5) でも同じです。

c03    alloc=300000  free=300000
c11    alloc=200000  free=200000   ( (5) or (6) の move 無し )
c11    alloc=100000  free=100000



2013/02/12
SoC spec list

追加しました。

SoC spec list

その他の表はこちらから

OpenGL まとめ


●Bluetooth

前回の記事では未対応と書きましたが、その直後 2013/02/02 以降の ROM
Bluetooth が使えるようになっています。

ubuntu wiki: Using the Device

給電しながら外部キーボードやマウスが使えるようになりました。


●Nexus 7 に Android 端末をつなぐ

SDK を install できるわけではありませんが adb は使えます。

sudo apt-get install android-tools-adb

USB Host に他の Android 端末をつないで adb で接続できます。
端末によっては充電しようとするので、セルフパワー(ACアダプタ付き) USB ハブを
経由した方が良いと思います。

nexus7ubuntu3.jpg

↑HTC J butterfly HTL21 をつないで "adb devices" や "adb shell" 等が可能です。
Host の Nexus 7 (RAM 1GB A9 1.2GHz) よりも butterfly (RAM 2GB Krait 1.5GHz)
の方が性能が高いので本当は逆にしたいところです。


●3D / OpenGL ES 2.0

OpenGL でないため GLX は動作しないものの、
描画には OpenGL ES v2.0 + EGL が使われています。

NVIDIA: Linux For Tegra

↑上の NVIDIA サイトのドキュメントにあるように mesa-utils-extra を入れると
OpenGL ES 2.0 を使ったアプリの動作を確認できます。

sudo apt-get install mesa-utils-extra
/usr/bin/es2gears

nexus7ubuntu2.jpg

glxinfo の代わりに es2_info コマンドでドライバの確認ができるようです。

WebGL も試しましたが、Firefox/Chromium とも動作しませんでした。
フラグ自体は有効に出来ます。


●software

Software center の Installed tab が開きませんが、
Unity の dash 画面でアプリケーションの uninstall ができます。
Launcher 一番上のボタンで dash 画面を開いてタブを切り替え、
Installed の中でアイコンを右クリックです。


関連エントリ
Android Tablet Nexus 7 上に開発環境をつくる (Ubuntu)


Bitbucket などクラウド系のリポジトリサービスを使うと、
どこでもソースコードをチェックできるので非常に便利です。
電車の中や外出時だろうと、ちょっとした空き時間にブラウザだけで
コードを追うことができます。

そのうち欲が出てきて、どうせならこのままコードを手直ししたいとか
コンパイルもできたらいいのに、とか思うようになります。

最近のモバイルデバイスは非常に性能が高いので、
クロス開発でなく自分でコンパイルしてもそれなりに速いはずです。

Android 端末なら各種 Linux 環境を install できそうなので試してみました。

nexus7ubuntu1.jpg

Nexus 7 で動く Ubuntu 13.04


●開発環境として

gcc, clang はもちろん、python, git, mercurial などそのまま使えるので
ライブラリのコンパイルや arm CPU のテストには十分です。
RAM は 1GB しかありませんが、CPU も 4core なのでコンパイルも予想より
高速でした。

パフォーマンスも NetWalker の時代とは比べ物にならず、
Atom NetBook でコンパイルするよりも快適です。

実際にコンパイル速度を比べてみました。

               CPU         OS         HW thread Compiler     実行時間
---------------------------------------------------------------------
1. Nexus 7     (Cortex-A9) Ubuntu arm 13.04  4  clang 3.2    12.5 sec
2. Nexus 7     (Cortex-A9) Ubuntu arm 13.04  4  gcc 4.7.2    18.2 sec
3. Optimus Pad (Cortex-A9) Ubuntu arm 12.04  2  clang 3.0    21.2 sec
4. Optimus Pad (Cortex-A9) Ubuntu arm 12.04  2  gcc 4.6.3    29.2 sec
5. VAIO type P (Atom Z540) Ubuntu x86 12.10  2  clang 3.0    26.1 sec
6. VAIO type P (Atom Z540) Ubuntu x86 12.10  2  gcc 4.7.2    33.5 sec
7. VAIO type P (Atom Z540) Windows 7 x86     2  vs2012       87.8 sec
8. NetWalker   (Cortex-A8) Ubuntu arm 9.04   1  gcc 4.3.3    91.2 sec
9. Mac HDD(Core i7-3615QM) Windows 8 x64     8  vs2012        5.1 sec
10.Mac HDD(Core i7-3615QM) Win8 x64 + NDK    8  gcc 4.7       4.9 sec
11.Mac HDD(Core i7-3615QM) Win8 x64 + NaCl   8  gcc 4.4.3     4.1 sec
12.Mac HDD(Core i7-3615QM) Win8+VMP+u12.10   4  clang 3.0     1.6 sec
13.Mac HDD(Core i7-3615QM) Win8+VMP+u12.10   4  gcc 4.7.2     2.6 sec
14.Mac HDD(Core i7-3615QM) Win8+VMP+u +NaCl  4  gcc 4.4.3     3.0 sec
15.Mac SSD(Core i5-3210M)  OSX10.8 x64       4  clang 4.2     2.3 sec
16.Mac SSD(Core i5-3210M)  OSX10.8 x64 +NDK  4  gcc 4.7       6.4 sec
17.Mac SSD(Core i5-3210M)  OSX+Para+u12.10   2  clang 3.0     3.0 sec
18.Mac SSD(Core i5-3210M)  OSX+Para+u12.10   2  gcc 4.7.2     4.4 sec
19.Mac SSD(Core i5-3210M)  OSX+Para+u +NaCl  2  gcc 4.4.3     4.1 sec
20.Mac  (Core2 Duo P7350)  Ubuntu x64 12.10  2  clang 3.2     4.3 sec
21.Mac  (Core2 Duo P7350)  Ubuntu x64 12.10  2  gcc 4.7.2     6.8 sec
22.Mac  (Core2 Duo P7350)  Ubuntu x64 + NDK  2  gcc 4.7      10.0 sec

実行時間が少ない方が高速

・NDK= Android 向け build (NDK r8d)
・NaCl= Native Client 向け build (pepper_23)
・VMP=Windows の VMware Player 上で Ubuntu 12.10 x64 (4core)
・Para=MacOS X の Paralles8 上で Ubuntu 12.10 x64 (2core)

C++ の lib のコンパイルで Debug/Release 両方生成しています。

すべてコマンドラインからのビルドで HW thread の数だけ並列化しています。

CPU だけでなくストレージの速度なども大きく影響しているので一概には言えませんが、
このサイズで持ち歩けてこの速度でビルドできるなら満足です。
用途にもよりますが、すでに "動かしてみた" レベルではなくなっているといえます。


spec 詳細
  Nexus 7           Tegra3 Cortex-A9  x4      1.2GHz   RAM   1GB
  Optimus Pad L-06C Tegra2 Cortex-A9  x2      1.0GHz   RAM   1GB
  VAIO type P       Atom Z540         x1 HT2  1.83GHz  RAM   2GB
  NetWalker PC-Z1   i.MX515 Cortex-A8 x1      0.8GHz   RAM 0.5GB
  Mac HDD           Core i7-3615QM    x4 HT8  2.3GHz   RAM  16GB
  Mac Book SSD      Core i5-3210M     x2 HT4  2.5GHz   RAM   8GB
  Mac mini HDD      Core2 Duo P7350   x2      2.0GHz   RAM   8GB


● Nexus 7 で動く Linux 環境

LINUX ON ANDROID
AndroidLinux.com
Ubuntu Nexus 7 Desktop Installer

大きく分けて二通りあり、Android として boot したままソフトウエア環境を
置き換えるタイプと、Native に最初から Linux として boot するタイプです。


前者は Android が動作したまま、chroot したプロセスのみが
あたかも Desktop Linux であるかのように振舞います。
Android 環境と併存するため比較的抵抗が少ないですが、
母体となる Android の root 権限が必要となるため、
何らかの方法で取得しておかなければなりません。

冒頭のコンパイル速度比較にある Optimus Pad (L-06C) はこちらです。
Android 上から起動して ssh 経由でコマンドラインのみ使用しています。
Window System が Native なものではないので GUI は期待出来ませんが
コマンドラインベースなら十分使える印象でした。


後者は bootloader を unlock して完全に新たな OS で置き換えます。
PC の HDD を消してクリーンインストールするようなものです。

実際は OS を置き換えなくても、Android の起動イメージを残したまま
boot 時のセレクタで切り替えることができるらしいので試してみました。
↓下記ページを参考にさせて頂きました。

Nexus 7でUbuntuをUSBメモリにインストールしてデュアルブートで起動して使う方法。

USB メモリがなくても内蔵ストレージにも共存可能な形で install できました。

xda-developers: [WiFi&3G] MultiROM v7 - updated recovery (Feb 02)

bootloader を unlock したあとに Multi boot 可能な bootloader を入れて、
install 機能を持った recovery tool を用いて OS image を書き込みます。
もちろんこれらの作業はすべて自己責任で行うことになります。


●使ってみて

Window の描画や文字が一部欠けたりとまだ不安定なところがあります。
その代わり HW アクセラレートがきいておりそれなりに高速です。
VAIO type P (Z500系 Atom) に入れた Ubuntu 12.10 よりも快適です。

タッチ操作できるようにカスタマイズされておりスクリーンキーボードも使えます。
きちんと日本語キーボードの配列に切り替わりますが、なぜか '_' の入力ができません。
縦画面にも回転できます。

ubuntu Using the Device

現時点 (2013/02/03) で Bluetooth がまだ使えないようです。
USB Host ケーブルを使えば USB 外付けの Mouse や Keyboard をつなげることができます。

Ubuntu の豊富なソフトウエアを利用できる点が魅力です。
試しに blender を install してみましたが、GLX が無く起動しませんでした。
gimp は動きました。

Chromium ブラウザをしばらく使っていると画面全体が壊れることがあります。
おそらくメモリ不足なので、swap を作った方が良いのかもしれません。

Tegra4 の Cortex-A15 や Qualcomm Krait 等、新しい世代の CPU では
I/O 含めてさらに高速に動作するはずです。
PC 無しに、Android アプリの自己開発ができるようになると面白いかもしれません。


続きます「Nexus 7 上に開発環境をつくる (2) Bluetooth と OpenGL ES 2.0」