日別アーカイブ: 2007年8月21日

Direct3D 10 Shader4.0 補間レジスタ数と速度の関係

コードを書いていて気になったので少々実験してみました。
VertexShader または GeometryShader が出力する値はラスタライザ
(RasterizerStage) に渡ります。
PixelShader は各ピクセルごとに補間された値を受け取ります。

この VertexShader の出力数値の数と描画速度の関係を調べてみました。
ここでは GeometryShader は NULL にしています。

横軸がシェーダーで使用した出力レジスタの総数、
縦がかかった時間(usec)です。

出力レジスタ数と速度の関係

出力数 命令数  88GTS   88GTX
   2     12     145     136 usec
   3     13     150     130
   4     15     147     138
   5     16     148     140
   6     17     148     134
   7     18     148     140
   8     19     157     135
   9     20     164     135
   10    21     173     135
   11    22     335     221
   12    23     347     229
   13    24     360     237
   14    25     376     248
   15    26     398     263
   16    27     410     271

・88GTS = GeForce8800GTS 640 の結果
・88GTX = GeForce8800GTX の結果
・GTS固定 = GeForce8800GTS で、出力レジスタ数を2個固定にし、
 命令数だけ増やしていったもの。

88GTS で出力レジスタ数が2~7個の間特に変化がないのは、動作時間へ
与える影響が他の実行ボトルネックに隠れてしまったためと考えられます。
7~10 までの緩やかな上昇がそれ以前も継続していた可能性が高いです。

その緩やかな上昇の要因として、出力レジスタ数の増加だけではなく
実行する命令数そのものが増えたことも考慮しなければなりません。

そのため出力レジスタ数を2個固定にして、プログラムの長さ(slot数)
を 12~27 まで変化させて同じように速度を量ってみました。
それがグラフ中の「GTS固定」です。
これを見るとほぼ一定なので、やはり10個以前でも出力レジスタ数が
動作に何らかの影響を与えている可能性があります。

注目すべきところはやっぱり出力数 10を超えたところで生じる急激な
変化です。ハード的なバッファの限界、同時に実行可能な補間ユニット
の数、内部バス転送能力の限界、等が考えられます。

VertexShader そのものの命令数が増加して実行時間が長くなれば、
それだけ Rasterizer にも余裕ができます。当初は、その拮抗点が
ちょうど10個目の位置にあったのではないかと仮定しました。
だけど「GTS固定」の結果を見ると、この程度の命令数ではほとんど
実行に影響を与えていないことがわかります。
よって 10 はハード的なマジックナンバーである可能性が高いです。

なお、あとから GeForce8800GTX でも同等のテストを行ってみましたが、
10個を越えた時点で発生する大きな変化は一緒でした。
(グラフに加えました)

ShaderModel 毎に対応している補間レジスタ数は次のとおりです。

ShaderModel2.0
・clmap された COLOR0~1 (2個)
・TEXCOORD0~7 (8個)
・POSITION、FOG

ShaderModel3.0
・自由に割り振れる 12個 (o0~11)

ShaderModel4.0
・自由に割り振れる 16個 (o0~o15)

ShaderModel 2.0~3.0 では、たぶんほとんどのケースで出力レジスタ
数は 10個以内に収まってしまうでしょう。
GeForce68/78 など 3.0 当時の設計でも、仕様上の上限よりは若干低い
位置にハードウエア的な制限があったと考えられます。
GeForce8800 で従来のシェーダーも非常に高速なのは、もしかしたら
こんなところにも原因があるのかもしれません。

今回のテストの結論は、もし速度を追求するなら出力レジスタ数は
10個以内に収めましょう、ということです。

ただ、これはあくまでボトルネックになる得る条件の1つに過ぎないので、
通常はピクセル面積ももっと大きいし、VertexShader も長くなるしと
他の実行サイクルの影に隠れてあまり表に出てこない可能性があります。

相殺されている分には実質的な負荷ではないので、高速化を考える場合
はこのあたりのさじ加減が難しいですね。