シェーダーとスレッド化

GeForce8800 になって Shader Unit のマルチスレッドがどうこうといわれるように
なったわけですが、スレッド化そのものは ATI(AMD)系だろうとかなり前から
使われているテクニックです。

この辺の話は後藤さんの記事でかなり突っ込んで紹介されています。
http://pc.watch.impress.co.jp/docs/2006/1121/kaigai320.htm

この記事で書かれている3つのスレッディングのうち「カスケード」と呼ばれている
ものは割と初期の段階から用いられています。後藤さんの説明では「メモリアクセス」
のレイテンシを隠蔽するためとなっていますが、これはもともとシェーダーの
パイプラインを埋めるのが目的です。

例えばシェーダー命令で直前の命令と依存関係が発生する場合、アウトオブオーダー
実行でない限りパイプラインの終了を待つためにストールが発生します。

dp3 r0.x, r2, c5
rsq r1.x, r0.x

この例では dp3 の d0.x の演算が完了するまで rsq は実行できません。命令上は
順番並んでいても、パイプライン化が行われているためすべての命令実行は半分以上
がオーバーラップしているからです。

仮にパイプラインステージ数が5段と仮定するとイメージ的には次のようになります。

・依存関係がない場合
dp3 F D E E W _
rsq _ F D E E W

・依存関係がある場合
dp3 F D E E W _
rsq _ F D _ _ E E W

前の命令完了を待つため数サイクル分のストール(パイプラインの空き)が生じます。
そのため動作速度を上げるには、命令を並べ替えて依存関係が発生しないように
プログラムを改良する必要があります。
(なお、この例ではレジスタの読み書きに関するストールなので、実際はバイパス化
でもうちょっと効率が上がるかもしれません)

シェーダーでは依存関係がない複数の演算が同時に大量発生するので、複数のコン
テキストを用いたスレッド化(カスケード化)でパイプラインを埋めることができます。

例えば 3スレッド(カスケード)で頂点演算する場合 V0~V2 の3頂点を同時に処理
します。

V0:dp3 F D E E W _ … (1)
V1:dp3 _ F D E E W _
V2:dp3 _ _ F D E E W _
V0:rsq _ _ _ F D E E W _ … (2)
V1:rsq _ _ _ _ F D E E W _
V2:rsq _ _ _ _ _ F D E E W _

V0,V1,V2 は依存関係が無いことがわかっているためストールは発生しません。

また V0 同士の場合でも、(1) の V0:dp3 演算のあと、(2) の V0:rsq 実行時には
すでに dp3 の演算が完了しているので、命令の並び上直接依存関係があっても見か
け上レイテンシ無しに実行することができます。
この場合特に最適化コンパイラや人間が並び替えで最適化する必要も無く効率が良い
ことがわかります。

レイテンシを考えながら書かなければならない他のハードと比べて、シェーダーの
プログラミングが便利で簡単なのはこのおかげだと思っています。

その代わり1つの実行パイプでもスレッド数分のコンテキストを保持しなければな
らず、またパイプラインが深くなればなるほどレイテンシを隠すためのスレッドが
余計に必要になることがわかります。

ShaderModel2.0 世代の GPU では、このリソース量の上限が実際の演算速度上の制限
になっていたようです。リソースが枯渇すると各スレッドの実行に必要なコンテキス
トの割り当てができず、実行できるスレッド数が減ってしまいます。

演算ユニットやパイプラインに余裕があっても、スレッドを発行できずに動作速度が
頭打ちになってしまうわけです。

昔の GPU で性能を謳う部分に「ハードウエアスレッド○○個」と書かれていたこ
とがありますが、このようにスレッド化はインオーダー実行時のレイテンシを隠す
ための機能なので、その分並列演算能力が増加するわけではありません。

スレッドが無くてもぎりぎりまで最適化したストールが発生しないコードは、
理論上は動作効率が変わらないことになります。
その分人間やコンパイラが苦労わけですが。

Cell の SPU(SPE) 命令を見て

暇なときに SPU(SPE) の命令を眺めたりしてましたが、普通の CPU ぽい命令が
かなりあって意外でした。というかレジスタが128bitでどの命令もSIMDになってる
だけでそれ以外は普通な感じです。
http://cell.scei.co.jp/

○演算は基本的に4種類
・8要素 16bit short int
・4要素 32bit int
・4要素 32bit float
・2要素 64bit double float

Intel でいえば全部 SSE で書いてるようなもの。x64 以降は fpu ではなく浮動
少数演算には常に SSE を使うらしいので割と近いのかもしれません。そういえば
VisualC++ も 2005 版になってデフォルトで SSE を使うようになってますね。

もちろん SPE は 3オペランドなので、その点は SSE 系より使いやすそうです。
レジスタも多いし。

もうひとつ意外だったのが、浮動少数演算系の命令が少なくて本当に必要最小限
であること。GPU の Shader のような至れり尽くせりのベクトル演算を期待すると
少々肩透かしを食らいます。(Shader の高度な演算も内部では複数の演算に分解
されている可能性はあります)

初期の SSE のように水平加算系の命令はなく、内積演算等でも同じようにベクトルの
並べ替えが発生するようです。4ベクトル同時演算の SIMD を活かすにはやっぱり
AoS SoA 変換も必要らしい。

GeForce8800(G80) がスカラー単位の演算ユニットに分離したのとは方向性が異なり
ます。そういえば G80 では内積などの水平系命令は演算ユニットをまたぐわけですが
どのような実装になってるんでしょう。

ハードの進化を否定するのはいいことなのか

ゲームの開発が大変になったと良く聞きます。ハードの性能が上がってより高い
クオリティのものが作れるようになったし、同時にユーザーが求める内容も
あがります。
その分だけ物量も必要で開発人数も増えて規模が大きくなりました。

ますます分業化が進んで個々の作業はさらに専門化し、作る人と考える人の距離が
遠ざかります。作り方を理解し作業工程まで工夫した設計がなかなかできず、
物量に頼る機会がますます増えてしまいます。

ハードの進化についていけずに、力技の対応で食いつないできたつけが回ってきたの
かもしれません。

開発がついていけないからといってハードの進化を否定するのは少々強引な気が
します。既存の環境やハードウエアなら使いこなしているから開発の負担が減る
だろうという考えも少々短絡的ではないでしょうか。

使っていればやっぱり不満点や足りない点が出てくるものです。あと少し、もう少し
だけここが改良されていればここまでできたのに、あともうちょっとメモリがあれば、
もうちょっと CPU が速ければ、クオリティがあがるのに、などなど。

進化しなければその同じ制限に再び悩まされることになります。世の中では、
例えば PC では当たり前に解決されていることであっても、進化を止めてしまったら
古い手法を使い続けなければなりません。

PC のスペックはものすごい速さで進化しています。10年前と比較すると 40倍の
CPU 速度に 20~30倍のメモリ量に、10倍の HDD 容量などなど。だからとって
プログラマの作業が極端に大変になったかといえばそうでもありません。

ハードが進化した分制限が無くなり、メモリや CPU速度に余裕が生まれて新しい開発
手法も使えるようになっています。class ライブラリが整備され、デバッガも高機能
になって、スクリプト言語による開発も幅広く用いられるようになりました。

メモリやディスクを大量に消費する重いアルゴリズムでも以前と比べると気楽に
使えます。

もちろん新しいことを覚え続けなければならないのは当然だけど、むしろ制限は
減ってきています。

前の世代でぎりぎりまで使い切って、限界が見えていればいるほどハードの進化は
歓迎すべきものでしょう。もしかしたら使いこなせていないときは、ハードの進化
が重くのしかかってくるのかもしれません。

同じように、データの作成手法でもさまざまな変化が必要でしょう。性能があがった
ことでいくらでもデータの作りこみができるようになりました。

2D 時代に例えれば色数に制限が無くなったようなもので、描こうと思えばどんな
絵でも出ます。パレットやチップが足りないといった言い訳はできなくなったし、
本当に力を入れるべき表現のポイントを絞らなければならないでしょう。

また不要な手間はできるだけ省き、人間が手でやる必要が無い部分は徹底的に
ツール化していく必要があるでしょう。

リアルタイム 3D CG の進化はまだまだこれからです。始まったばかりです。

PS3 の Linux

Playstation3 で動く Linux について以前公式にニュースになっていましたが、
すでに動作するものが入手できるようです。

Impress の YDL の記事 2006/10/17

http://www.fixstars.com/
http://cell.fixstars.com/ps3linux/index.php/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8

Linux PC としても実用的なのかどうか、
またはリビングに置いた安価な PC として使えるのかどうか、
などといった実用面はさておき・・

一般ユーザーレベルにも Cell プログラミングの機会が与えられた点はかなり
興味深いところです。

ゲーム機などの専用ハードを、一般向けにもプログラミング用途で開放することは
これまでもありました。でも魅力的なものはそれほど多くはありませんでした。

(1) 発売後かなり期間がたってから、すでにハードとして枯れた状態で公開
(2) 専用のツールや言語を通した限定的なものでハードは直接見えない

発売直後は価格的にも性能的にも PC をはるかに凌いでいても、何年も経ってから
では PC に追い抜かれています。PC 以外をわざわざ使う意義はかなり薄くなって
しまいます。

ところが Playstation3 の場合は発売したばかりです。非常にスペックも高くて
まだまだ本業の開発メーカーもハードを完全に使いこなしていません。そんな中で
ハードに触れるようにするのだから、誰もが平等に Cell プログラミングのスタート
ラインに立てるということです。

面白そうだし、Cell 普及のためとはいえこの流れを作った SONY もみごと。
優秀なプログラマが育ってくれるとうれしいですね。

Gears of War がすごすぎる

Xbox360 のゲームソフト Gears of War のグラフィックがすごすぎます。
http://www.xbox.com/en-US/games/g/gearsofwar/
Unreal Engine 3 の本家 Epic Games 開発なので、もちろんエンジンそのものが
最初からすごいんだけど…。

とにかくその Engine 性能を限界まで引き出すすばらしいデータの数々。
キャラもマップも今まで見たことが無いくらい、緻密で作りこんであって、
それでいてプログラムの性能を活かしきる。
エフェクトやシェーダーの効果もさりげなくて実に効果的です。

見たのは 2006/11/7 発売の海外版だけど、
日本語版も 2007/01/18 に発売されるようです。
http://www.xbox.com/ja-JP/games/g/gearsofwar/

今まで見たゲームの中では最高の緻密さ。圧倒的なクオリティです。
これをもっと大々的に宣伝したらもうちょっとは Xbox360 もいけるんじゃないかと
思ってしまいました。

○今日のシェーダーメモ

mul( float4x4, float4 ) と mul( float4, float4x4 ) の違い

// mul( float4x4, float4 )
mul r0, c1, v0.y
mad r0, c0, v0.x, r0
mad r0, c2, v0.z, r0
mad o0, c3, v0.w, r0

// mul( float4, float4x4 )
dp4 o0.x, v0, c0
dp4 o0.y, v0, c1
dp4 o0.z, v0, c2
dp4 o0.w, v0, c3