前回はRegister Fileまで説明したので、今回はその先を御紹介したい。

前回: 【レポート】IDF Fall 2010 - 大原雄介の「Sandy Bridge」徹底解説・その1

Out-of-Order部(2)

Register Fileに加えてSchedulerに関しても改善がなされたが、これは塩田氏の記事でほぼ語りつくされており、要するにIn Flightできる命令数を大幅に増やすと共に、Load/Store Bufferを強化している。PRFを使った事で、Bufferそのものに費やすトランジスタ数は大幅に減っている筈で、ポインタとして利用するSmall Bufferの他、配線の増加などはあるにしても、Buffer自身の数を増やしてもトランジスタ数へのインパクトは少ないと考えられる。

さて、本題はその先である。ここからはOut of Order部となるわけだが、ここでの設計ターゲットは「よりIPCを向上する」(Photo01)という、至極真っ当なものとなっている。とはいっても、基本的にはNehalemの内部構造をそのまま踏襲しており、変更箇所は余り多くない(Photo02)。まずALU/FPUについてであるが、Photo03にも明記してある通り、整数演算に関してはほぼ手付かずである。整数演算性能に関しては、ALUそのものを強化する前に、メモリアクセス性能を改善する方が先である(というか、こちらを上げれば更に整数演算性能が上がる)と判断したのであろう。勿論後述するように、個々の命令に関しては更にスループットを上げたものもあるが(Photo04)、全体としては概ねNehalemのままである。

Photo01: 通常IPCを向上するとバッファのサイズやデータ移動が増えるため、消費電力も同時に増えやすい。これをいかに抑えるかがキーである、という話。

Photo02: Port 0/1/5がALU/FPU、Port 2/3/4がLoad/Store Unitとなっている。黄色の部分がSandy Bridgeにおける変更箇所である。

Photo03: もっともFloating PointのTroughputを倍に、といってもSSEをAVXに拡張するだけで、その意味では本質的な違いは(何が本質的か、という議論はあるにせよ)ないことになる。

Photo04: 細かい改良は大分なされており、スループット増加のみならずレイテンシ短縮も図られているのではないかと思われる。こうしたものも積み上げることで、NehalemからのIPC向上に貢献しているのだろう。

で、主要な強化点である浮動小数点演算性能の倍増であるが、これはAVXをサポートすることによって実現される(Photo05)。AVXは既に多くレポートがある通りで、同時演算処理幅をSSE系の128bitから256bitに倍増させており、加えて3オペランド命令や様々な演算オプションを追加したことで、SSE系よりも大幅に性能を引き上げることを狙ったものである。このインプリメントがちょっと面白い。Photo06にあるように、Port 0/1/5は各々複数の処理が割り当てられている形だ。例えばFP ADD「だけ」が続く場合は、Port 1だけにひたすら処理が割り当てられる形になるが、もう一つの視点はGPR/SIMD INT/SIMD FPのどれか? という話である(Photo07)。これがSandy BridgeではGPRとAVXという2つのモードになったというのも興味深い(Photo08)。このスライドの最後に、265bitの乗算と加算、それとロードが同時に行えるという書き方をしているが、これがかならずしも全部IntとかFloatとは限らないところがミソである。つまりPort 0/1ではFloatとしてAdd/Multiplyを行うとして、Port 2/3ではIntとしてLoadが実行できる、といった意味に捉えるのが正解だと思う。

Photo05: 逆に言えばAVXを除くと、それほど大きな差は無い、という言い方もできる。

Photo06: GPRは汎用レジスタ(General Purpose Register)、SIMD INT/SIMD FPはどちらもXMMレジスタを対象とするが、内部のデータの持ち方が変わる事になる。

Photo07: 幾つかの命令、例えばShuffleなどはFPでは提供されないためか、あくまでもIntegerのみで利用する前提になっている。

Photo08: これを見れば、ロードそのものを256bitに拡張するのではなく、128bitのロード×2にしたほうが効率が良いのが理解できる。

さて、そのAVXの強化点についてもうすこし。AVXは従来のSSEの完全に置き換え、という使いかたを想定している。このため、256bitをフルに使った演算以外に、下位128bitのみを使った演算もサポートされている(Photo09)。で、128bitに関してであるが、まず大きな違いは(上でも触れた)3オペランド命令が追加されたことだ。もともとx86では2オペランド命令のみだった。理由はレジスタの節約である。特に初期の(つまり8008とか8080などの)プロセッサの場合、レジスタを増やすと回路が複雑になるため、なるべくレジスタの使用量を減らす事が求められた。そこで、

C = A + B

といった演算をサポートせずに、代わりに、

A = A + B

のみを実装している。この結果として、Aは演算をすると必ず書き換わってしまう、というのが問題であった。特に64bitになって汎用レジスタの数が倍増、普通にプログラムで利用できるレジスタの数では3倍以上に増えると、この演算を掛けると片方のレジスタが必ず書き換わるという流儀がむしろ効率を落とすものになってきた。

Photo09: 様々な置換命令も追加された。128bit単位での置換というのは、128bit演算での利用を前提にしているのであろう。

こうした事はAMDも気がついており、AVXが発表される1年前にアナウンスされたSSE5で、3オペランド及び4オペランド命令をサポートした。この4オペランド命令というのは、

A = B × C + D

というFMAC(Fused Multiply-Accumulate)などの代表されるものであるが、他にも、

C = A + B

を行うときの細かなパラメータ制御をDという4つ目のオペランドで指定する、なんてケースもある。こうしたものもAVXに取り込まれることになった形だ(Photo10,11)。他にもAVXではInsertion/Extraction(挿入/取り出し)やBoradcast、Conditional Loadなど幾つかの特殊命令が追加されている(Photo11)。

Photo10: 上側は左が従来の2オペランド命令を利用した場合のコード、右がAVXの3オペランドの例。下側は4オペランド命令の例。

Photo11: 何れも、「別にプログラムで出来るけれど手間が掛かる」処理ばかり。それはいいのだが、MMX→SSE→AVXと、どんどん命令数が肥大する傾向にあるのが、それでいいのかどうかちょっと微妙に感じる。

これを支える実行ユニットの構造であるが、従来のもの(Photo12)から大きく変えずに(Photo13)実装された。効率やダイサイズを考えなければ256bit幅に演算ユニットを拡張してしまうのが一番早いのだろうが、消費電力を抑えるといったニーズを考えると、256bit幅全体をまとめて操作する可能性のあるShuffle以外に関しては256bit幅が必須だが、その他については128bit幅のままにしておけば、SSE命令や128bitのAVX命令を効率よく実行できることになる。Load/Storeユニットがダブルになったのも、主要因はやはりこのAVXの256bit幅に対応することにあると思われる。

Photo12: 従来の構造。それぞれ128bit幅で実装される。

Photo13: 流石にShuffleだけは256bit幅にしないとどうしようもないだろう。