Previous: Assembly Loop Unrolling, Up: Assembly Coding [Index]
ここではアセンブラで,リム配列の処理を行うソフトウェアパイプラインループの書き方を案内します。
まずは,アルゴリズムと必要となる命令を決め,展開もスケジューリングもしないコードを書き,きちんと動作することを確認します。3オペランド命令が使えるCPUでは,新しい値一つ一つに対して新しいレジスタに書き込むようにしておくと,後に続くステップが簡単にできます。
この時,各命令に対して関数ユニットが対応したり,I/Oポート要求を発行したりすることを覚えておいて下さい。 ある命令が2つのユニット,例えばU0もしくはU1の片方を使うことができるときは“U0/U1”というカテゴリーを作ります。 それぞれのユニット(あるいか統合したユニット)を使ってそのトータルを数え上げ,しかる後,全ての命令を数え上げます。
これらのトータル数からベストなループ時間を導きます。最終的には,完全に命令レイテンシを隠せる完ぺきなスケジューリングを目指すことになります。トータルの命令数はその限界を示すものとなるか,あるいは特定の関数ユニットになります。命令をいじくることで,その限界を軽くすることができるかもしれません。
ループ回数をNとすると,命令の束をN回発行し,最終ループに終了のためのループ分岐を持たせます。 この命令の束として,望ましい関数ユニットを用いてダミーの命令で埋めてみましょう。これを動かすことで,到達可能な速度が確認できるようになります。
さて,高速だが意味のないダミーの命令を実際の命令に置き換え,低速だが正しいループにして動かしてみましょう。最初はまずロード命令から始まるのが普通で,次にロードした値を使った命令に置き換えていきます。このループをもう一度動かし,目標とするスピードに達しているかどうかをチェックします。
置き換えた命令はそのままにして,ループの計測を頻繁に行うようにします。何度目か繰り返した後, ループ内の最後から最初まで書き換えを完了させます。新しい値には新しいレジスタを使うという戦略を使うと, レジスタの衝突は起こらなくなります。これをしないのであれば,一度使用したレジスタをぶち壊すことがないよう注意して下さい。 一度使用したレジスタを書き換えるのはエラーの元になりかねません。
この書き変えたループは,もとのループを1,2回重ねて実行し,ベクトルの一要素の計算を新しいループの最初の1回目開始し, 更に数回以上繰り返して完結させて下さい。
最終段階では,次々に実行されて終了する(feed-in and wind-down)コードをループ用に生成します。そのためには,スタート時にループのコピーを行い,有効な先行命令を持たない命令群を一切削除します。そして,最後では,望ましくない結果(一切のロード命令も含む)をもたらす命令群を削除します。
ループはロードして処理する最小のリム数を保持するようにしますので,次々に実行される(feed-in)コードは要求サイズを小さくし,終了処理(wind-down)のための部分,もしくは小さいサイズのための特別なコードは飛ばして,テストしなければなりません。