第12世代インテル® Core™ プロセッサーのPコア(高性能コア)はGolden Coveというマイクロアーキテクチャでできていますが、Golden CoveにはFADD演算器が新規搭載されました。 FADD演算器は、浮動小数点数加算を高速に行う演算器であり、そのレイテンシは2 cycleになっています。 これは今まで浮動小数点数加算が4 cycleだったのと比べると、2倍速くなったことになります。
浮動小数点数演算器はパイプライン化されているため、レイテンシが改善されてもスループットとピーク性能は変わりませんが、浮動小数点数加算を多く用いるプログラムの高速化が期待できます。 (浮動小数点数乗算をあまり含まないのにもかかわらず)浮動小数点数加算を多く用いるプログラムとしては、倍倍精度(疑似四倍精度、double-doubleとも)を用いたプログラムが挙げられるでしょう。 実際、倍倍精度の計算を行うプログラムは、かなり高速化するようです。
この記事では、FADD演算器に関連することを特にまとめずいろいろ書いていきたいと思います。
測定環境
浮動小数点数演算レイテンシの歴史(AVX以降)
Golden Cove以外の情報は、uops.infoで公開されているものを利用しました。
マイクロアーキテクチャ | 浮動小数点数加算 | 浮動小数点数乗算 | 浮動小数点数積和演算 |
---|---|---|---|
Sandy Bridge/Ivy Bridge | 3 | 5 | N/A |
Haswell/Broadwell | 3 | 5 | 5 |
Skylake以降 | 4 | 4 | 4 |
Golden Cove | 2or3 | 4 | 4 |
Zen+ | 3 | 4 | 5 |
Zen2 | 3 | 3 | 5 |
Zen3 | 3 | 3 | 4 |
スループットなどの情報は、AVX/AVX2によるFMA - Qiitaがまとまっていておすすめです。
スケジューラの戦略
zmmを使わない場合
FADD演算器はPort1とPort5に配置されています。 スケジューラは、vaddsd命令(丸めモード指定があってもよい)やzmmを使わないvaddpd命令を必ずPort1かPort5に割り当てます。 したがって、プログラム上なにも工夫しなくても、浮動小数点数加算は低レイテンシで実行されます。
zmmを使う場合
zmmを使うvaddpd命令*1がどのポートに割り当てられるかはよくわかりません。 平均的なレイテンシが整数にならないため、レイテンシが異なる複数のポートで実行されるものと思われます。
Port0を埋めるために無駄なシフト命令*2を挿入すると、以下の現象が起こります。
- レイテンシが改善することがある。3.2cycleが2.4 cycleになる
- スループットが改善することがある。最良で1.5命令/cycle程度になる
Port1を埋めるために無駄な整数乗算命令を挿入すると、以下の現象が起こります。
- レイテンシが改善することはない
- スループットが改善することがある。最良で1.5命令/cycle程度になる
この現象から推測すると、以下のように割り当てられているのではないかと推測します。
- zmmを使うvaddpd命令は、Port0, Port1, Port5に割り当てられる
- Port0に割り当てられた場合、4 cycleレイテンシ
- 従来通りPort1と共同でAVX512命令を処理するパターン?
- Port1が混んでいる場合、Port0だけでも処理できる?
- Port0はAVX512命令を処理できないので、μOP二つになると思われる
- Port1に割り当てられた場合、2 cycleレイテンシ
- Port0が混んでいる場合のみ?
- Port1はAVX512命令を処理できないので、μOP二つになると思われる
- Port5に割り当てられた場合、2 cycleレイテンシ
- Port5は単独でAVX512命令を処理できる
いつでも2 cycleではない
vaddsd命令の連鎖を実行する場合、一命令当たりのレイテンシは2 cycleになりますが、他の命令との組み合わせだと2 cycleにならないようです。 vmulsdとvaddsdが交互に並んだ命令列では、その二命令の合計レイテンシが6 cycleとなるのが期待されるところ、実測してみると7 cycleになり一致しません。
おそらく、FADD演算器の結果をFADD演算器に渡す場合のみ2 cycleレイテンシが実現され、他の演算器に渡す場合は3 cycleレイテンシとなります。 これは推測ですが、FADD演算器は演算結果を浮動小数点数で出力しないことで2 cycleレイテンシを実現しているのではないでしょうか。 具体的には、FADD演算器は加算器の出力とシフト量をそのまま出力しているのではないでしょうか。 浮動小数点数加算器の入力側には必ずシフタがあるので、FADD演算器の結果が浮動小数点数加算器に使われるのであれば、そのシフタで一緒にシフトしてしまえば問題ありません*3。 このようにすることで、シフタを通す回数が一回減るため、浮動小数点数加算を繰り返すループの計算時間が削減できます。 一方、浮動小数点数乗算器の入力側にはシフタがないため、そういった演算器に渡す際のデータ整形のために1 cycleのペナルティを受けると考えられます*4。
浮動小数点数加算ばかりやりたい場合
Golden CoveのPort0は、Haswell/Broadwellと同様に、浮動小数点数加算命令を受け取らず浮動小数点数積和演算命令を受け取るポートになっています。 したがって、このポートを活用すると実質的な浮動小数点数加算命令のスループットを2命令/cycleから3命令/cycleに増やすことができます。 もちろん、Port0を使った浮動小数点数加算はレイテンシが4 cycleになります。
vfmaddsd命令はPort0かPort1のどちらかに割り当てられる可能性があります*5が、Port0に割り当ててくれる*6ため、実際にスループットとして一サイクル当たり3つの浮動小数点数加算を行うことが可能です。
倍倍精度演算が速くなる
倍倍精度演算には、浮動小数点数乗算を含まない浮動小数点数加算の連鎖が多く出現します。 したがって、FADD演算器の追加の恩恵を大きく受けるプログラムであると思われます。
ちょうど最近、以下のような四則演算を網羅したベンチマークが提案されていました。
https://t.co/BVpPsVP4x8
— kashi (@mkashi) 2021年12月3日
加減乗除を一つずつ含んで無限に繰り返せるループが面白かったので、少しアレンジしてみた。
・初期値を0.4〜0.6の間に取るとずっとその間で乱数っぽく動く。
・べったりと依存性のある計算で、部分的な並列化ができない。
・FMA命令を使えそうなところがない。 pic.twitter.com/hNP5NICgwZ
このベンチマークをfloatとdoubleとdouble-doubleで実行してみたところ、floatの場合4.0秒、doubleの場合4.6秒、kv::ddの場合18.2秒(-DKV_USE_TPFMA
なしだと22.6秒)となりました。
double-doubleを使った場合のオーバーヘッドがdoubleを使った場合の四倍未満という結果になりました。
Intel Core i7 9700を用いた実験(変なベンチマークテスト - kashiの日記)では、オーバーヘッドは五倍程度という結果だったので、(FADD演算器の追加だけの効果であるかは確認していませんが)最新のCPUでは倍倍精度演算はかなり速いと言えそうです。