ML-KEMのNTTにおける乗算の最適化手法詳解2
初めに
前回ではML-KEMのNTTにおけるMontgomery乗算のAVX向け最適化手法を解説しました。
今回はAArch64用SIMDでの最適化手法を解説します。
Barrett reductionの改善
Barrett reductionは定数除算を逆数の近似値の乗算とシフトに置き換える手法です。
ここで整数
しかし、前回と同じく片方の値が定数であることを用いて
実際に使われている方法をSIMDの1プレーンで説明しましょう。
AVXでの実装
// z = round((y * (R/2)) / p)
// return (x * y) % p
int modp1(int x, int y, int z) {
int t1 = vpmulhrsw(x, z);
int t2 = pmullw(t1, p);
int t3 = pmullw(x, y);
int r = psubw(t3, t2);
return r;
}
まず事前計算として
vpmulhrsw(x, z) は vpmulhs と似ているのですがのですが、そのときに四捨五入する(
int vpmulhrsw(int x, int y) {
assert((-32768 < x && x < 32768) && (-32768 < y && y < 32768));
int prod = (x * y) + (R/4);
return prod >> 15;
}
これでうまくいく理由を確認しましょう。
すると
よって
したがって
AArch64での実装
AArch64のSIMD命令にはAVXの vpmulhrsw に相当する命令として sqrdmulh があります(
また AVXと異なり整数に対する積和演算を搭載するため、mls(t2, t1, p) = t2 - t1 * p を用いるとAVXより1命令減らせて
int modp1_aarch64(int x, int y, int z) {
int t1 = sqrdmulh(x, z);
int t2 = pmullw(x, y);
return mls(t2, t1, p);
}
int sqrdmulh(int x, int y) {
return vpmulhrsw(x, y);
}
int mls(int acc, int x, int y) {
return psubw(acc, pmullw(x, y));
}
とできます。AVXにも整数の積和演算命令があったらよかったですね。
実装実験
mlkem-nativeの実装は、現在AArch64は今回紹介した方法、AVXは前回紹介した方法を採用しています。
AVXでも今回の実装をするとワンチャン速くなるかもと思って実装してみましたが、変わりませんでした。
命令数とレイテンシ・スループットが同じなので変わらないのは当然なのですが、アルゴリズムがAArch64と同じになるのでプログラムの正しさの検証などはやりやすくなるかもしれません。
Discussion