IntelのFast Adder (FADD)について

第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演算器の追加の恩恵を大きく受けるプログラムであると思われます。

ちょうど最近、以下のような四則演算を網羅したベンチマークが提案されていました。

このベンチマークを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では倍倍精度演算はかなり速いと言えそうです。

*1:AVX512の命令はEコア(高効率コア)を無効にした時のみ使えます

*2:シフト命令はPort6でも実行されるが、Port6はSIMD命令を取り扱えないポートなので省略

*3:計算結果をIEEE754に従うように丸める必要はあります

*4:実際はFMA演算器の加算入力に渡しても3 cycleレイテンシだったので、他の演算器には手を加えていないのだと思われます

*5:ポート割り当ては他アーキテクチャと同じと仮定

*6:Port1が混んでいることを見抜いているのか、単にPort0が優先なのかは不明