SlideShare a Scribd company logo
1 of 27
Download to read offline
expの実装に見る
AVX-512とSVEの違い
2020/12/3
第9回HPC-Phys勉強会
光成滋生
• expの近似計算
• AVX-512による実装
• SVEによる実装
• レジスタリネーミング
• SVEのfexpaを使った近似計算
• 実装
• 逆数近似命令
目次
2 / 27
• 級数展開を使う
• 𝑒 𝑥 = 1 + 𝑥 +
𝑥2
2
+
𝑥3
6
+ ⋯
• 要請
• 級数展開における𝑥は小さいのが望ましい
• 2 𝑛(𝑛が整数)は高速に求められる
• 式変形
• 𝑒 𝑥
= 2 𝑥 log2 𝑒
• 𝑦 ≔ 𝑥 log2 𝑒 を整数部分𝑛と小数部分𝑎に分ける( 𝑎 ≤ 1/2)
• 𝑦 = 𝑛 + 𝑎
• 𝑒 𝑥 = 2 𝑦 = 2 𝑛+𝑎 = 2 𝑛2 𝑎
• 2 𝑎 = 𝑒 𝑎 log(2)
• 𝑏 ≔ 𝑎 log(2), 𝑏 ≤ log 2 /2 = 0.346
exp(float x);の近似計算(1/3)
3 / 27
• floatなら1e-6程度まで求まれば十分
• 5次の項まで計算
• 𝑒 𝑥
= 1 + 𝑥 +
𝑥2
2
+
𝑥3
6
+
𝑥4
24
+
𝑥5
120
• 係数補正
• 𝑓 𝑥 ≔ 1 + 𝑥 + 𝐵𝑥 + 𝐶𝑥2 + 𝐷𝑥3 + 𝐸𝑥4 + 𝐹𝑥5
• 𝑥 ∈ [−𝐿, 𝐿] where 𝐿 ≔ log(2)/2
• 𝐼 ≔ ‫׬‬−𝐿
𝐿
𝑓 𝑥 − 𝑒 𝑥 2 𝑑𝑥を最小化する(𝐵, 𝐶, 𝐷, 𝐸, 𝐹)を求める
• 1次の項(𝐴)を1に固定しているのは0次と共用したいから
• 誤差が概ね半分程度になる
• Sollyaのremezよりは誤差が小さい?
• L2ノルムとL1ノルムどちらがよいかアプリ依存?
exp(float x);の近似計算(2/3)
4 / 27
• まとめ
• 配列に対する一括処理
• テーブルを使わないためSIMD化しやすい
exp(float x);の近似計算(3/3)
input : x
x = x * log_2(e)
n = round(x) ; 四捨五入
a = x - n
b = a * log(2)
c = 1 + b(1 + b(B + b(C + b(D + E b))))
y = c * 2^n
output : y
void expv(float *dst, const float *src, size_t n) {
for (size_t i = 0; i < n; i++) dst[i] = exp(src[i]);
}
5 / 27
• Intelの512bit幅のSIMD命令セット
• 32個の512bit AVXレジスタ
• double x 8, float x 16, int32 x 16, int16 x 32, int8 x 64などの型
• 概ね3オペランド
• op(r1, r2, r3) ; r1 = r2 op r3
• d ; 32-bit int, ps ; float, pd ; double
• 積和演算FMA
• d = a * b + cが望ましいが4オペランドになるので
AVX-512
paddd zmm0, zmm1, zmm2 // zmm0 = zmm1 + zmm2 as 32-bit int
addps zmm0, zmm1, zmm2 // as float
addpd zmm0, zmm1, zmm2 // as double
vfmadd132ps r1, r2, r3 // r1 = r1 * r3 + r2
vfmadd213ps r1, r2, r3 // r1 = r2 * r1 + r3
vfmadd231ps r1, r2, r3 // r1 = r2 * r3 + r1
6 / 27
• C++でCPUのニーモニックを記述
• 実行時にそれに対応する機械語が生成されて実行する
• 例 : 「整数nを足す関数」を生成する関数
• genAdd(5); // 「5を足す関数」が実行時に生成される
• 生成された関数
• exp自体は静的な関数でよいがIntel oneDNNではパーツの一つ
として実行時生成されている
Xbyak/Xbyak_aarch64
struct Code : Xbyak::CodeGenerator {
void genAdd(int n) {
// rsiはLinuxでの関数の第一引数
lea(rax, ptr[rsi + n]);
ret();
} };
lea rax, [rsi + 5] // + 5は即値
ret
7 / 27
• round関数
• vrndscaleps(dst, src, round_ctl)
• round_ctl ; 2bitフラグ
• 00 ; nearest even ; 一番近い偶数丸め
• 01 ; equal or smaller ; 切り捨て
• 10 ; equal or larger ; 切り上げ
• 11 ; truncate ; 0方向に切り捨て
• vrndscaleps(dst, src, 0) ; dst = round(src);
• c = 1 + b(1 + b(B + b(C + b(D + E b))))
• FMAを5回適用
• 𝑦 = 𝑐 × 2 𝑛の部分
• AVX2までは整数𝑛をビットシフトしてfloatに変換して...
• AVX-512ではvscalefps(dst, r1, r2) // dst = r1 * 2^r2
AVX-512によるexp
8 / 27
• 準備
• 下記変数はzmmレジスタのエリアス名
• log2_e, log2, one, B, C, D, Eは事前に定数設定
• t1, t2は一時レジスタ, xが入出力(x = exp(x))
exp一つ分
genExpOne() {
vmulps(x, log2_e); // x *= log2_e
vrndscaleps(t1, x, 0); // t1 = round(x)
vsubps(x, t1); // x = x - t1 ; 小数部分a
vmulps(x, log2); // a * log2
vmovaps(t2, E);
vfmadd213ps(t2, x, D);
vfmadd213ps(t2, x, C);
vfmadd213ps(t2, x, B);
vfmadd213ps(t2, x, one);
vfmadd213ps(t2, x, one); // t2 = 1+b(1+b(B+b(C+b(D+E b))))
vscalefps(x, t2, t1); // x = t2 * 2^t1
}
9 / 27
• ループnが16の倍数でないときの扱い
• マスクレジスタk1, ..., k7
• zmmレジスタのkに対応するbitが1なら処理, 0なら非処理
• T_zを指定すると非処理の部分に0が入る
• vmovups(zm0|k1|T_z, ptr[src]);
• readしない部分はメモリアクセスしない(アクセス違反しない)
• マスクレジスタを指定しないときに比べてやや遅い
• 16の倍数のときは指定しない方がよい
端数処理(1/2)
k1 = 0b00..01111111 (8bitの1)のとき
src | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9| a| b| ...
|← readしない →
zm0 | x0| x1| x2| x3| x4| x5| x6| x7| 0| | 0| 0|...
10 / 27
• コア部分
端数処理(2/2)
Label lp = L(); // メインループ
vmovups(zm0, ptr[src]); // zm0 = *src
add(src, 64); // src += 64
genExpOne() // zm0 = exp(zm0)
vmovups(ptr[dst], zm0); // *dst = zm0
add(dst, 64); // dst += 64
sub(n, 16); // n -= 16
jnz(lp); // if (n != 0) goto lp
L(mod16); // 端数処理
and_(ecx, 15); // n &= 15
jz(exit);
mov(eax, 1);
shl(eax, cl); // eax = 1 << n
sub(eax, 1); // eax = (1 << n) - 1
kmovd(k1, eax); // k1 = nビットの1
vmovups(zm0|k1|T_z, ptr[src]); // 残り読み
genExpOne() // zm0 = exp(zm0)
vmovups(ptr[dst]|k1, zm0|k1); // 残り書き
L(exit);
11 / 27
• float x[16384];に対するstd::exp(float)との比較(clk)
• Xeon Platinum 8280 2.7GHz, g++ 9.3.0 Ubuntu 20.04.1 LTS
• genOneExpをループアンロール
• fmath::exp 8.7clk→7.2clk
• https://github.com/herumi/fmath/blob/master/fmath2.hpp
ベンチマーク
std::exp fmath::exp
140.1 8.7
genExpTwo() {
vmulps(x0, log2_e);
vmulps(x3, log2_e);
vrndscaleps(t1, x0, 0);
vrndscaleps(t4, x1, 0);
vsubps(x0, t1);
vsubps(x3, t4);
...
12 / 27
• Armの可変長SIMD命令セット
• 128~1024(?)bitレジスタ
• double x 8, float x 16, int32 x 16, int16 x 32, int8 x 64などの型
• 概ね3オペランド
• A64FX(富岳のCPU)では32個の512bitレジスタz0, ..., z31
• movprfx(dstをsrcとして利用)
• predは述語レジスタ
• マスクレジスタ相当
• AVX-512と異なり常に指定
SVE
fadd z0.s, z1.s, z2.s // as float
add z0.b, z1.b, z2.b // as int8
movprfx(dst, pred, r1);
fmadd(dst, pred, r2, r3); // dst = r1 * r2 + r3
13 / 27
• SVEレジスタの各要素を処理する(1)か否(0)かを指定
• 例 ld1w(z.s, p/T_z, ptr(src));
• z = *src; // float x 16個読み込み
• i番目の各要素(i = 0, ..., 15)について
• 述語レジスタp[i] = 1ならz[i] = src1[i]
• p[i] = 0ならT_z(zero)を指定しているのでz[i] = 0
• T_zを指定しなければp[i]の値を変更しない
述語レジスタ
src x0 x1 x2 x3...
z.s x0 0 x2 x3...
p 1 0 1 1
14 / 27
• 「x4 + i < n」が成り立つ添え字までp[i] = 1にする
• 例 x4 + 16 <= nならi = 0, ..., 15についてp[i] = 1
• 全てのデータが有効
• x4 + 3 = nならi ≦ 2についてp[i] = 1, その他p[i] = 0
• p[i] = 0の部分はデータを読まない・書かない
• 読み書き属性が無い領域でも大丈夫
whilelt(p.s, x4, n);
i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
p 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
p 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
15 / 27
• メイン部分
• x4 + 16 ≦ nである限りp0[i] = 1 for i = 0, ..., 15
• ループの最終ではp0[i] = 1 for i <(n % 16), p0[i] = 0(otherwise)
• メリット
• SVEが256bitや1024bitでも同じコードで動く
• SVEはScalable Vector Extensionの略
• デメリット
• あまり速くない ; 結局AVX-512のように分けた方がよい
ループの終わり処理
Label lp = L();
ld1w(z0.s, p0/T_z, ptr(src1, x4, LSL, 2));// z0 = src1[x4 << 2]
...
incd(x4); // x4 += 16
L(cond);
whilelt(p0.s, x4, n); // while (x4 < n)なら
b_first(lp); // lpラベルにジャンプ
16 / 27
• AVX-512とほぼ同じ
• 述語レジスタpはall 1に設定
• round関数はfrintn
• fscale(x, p, n)のnがfloatではなくintなのでfcvtzsでintに変換
sveによるgenExpOne
fmul(tz0, tz0, log2_e);
frintn(tz2, p, tz0); // round : float -> float
fcvtzs(tz1, p, tz2); // float -> int
fsub(tz2, tz0, tz2);
fmul(tz2, tz2, log2);
movprfx(tz0, p, E);
fmad(tz0, p, tz2, D); // tz0 = tz2 * E + D
fmad(tz0, p, tz2, C);
fmad(tz0, p, tz2, B);
fmad(tz0, p, tz2, one);
fmad(tz0, p, tz2, one);
fscale(tz0, p, tz1); // tz0 *= 2^tz1
17 / 27
• frintnなどの命令のあとの依存関係
• レジスタ名を入れ換えると2倍速くなるケース(74→32nsec)
• https://github.com/herumi/misc/commit/0362d5647f693be66
da841eea7ca333d0f5b5329
レジスタリネーミングが苦手?
18 / 27
• 利用レジスタを増やしてループ展開
ループアンロール
// n = 1 or 2 or 3
for (int i = 0;i<n;i++) fmul(t[0+i*3], t[0+i*3], log2_e);
for (int i = 0;i<n;i++) frintn(t[2+i*3], p, t[0+i*3]);
for (int i = 0;i<n;i++) fcvtzs(t[1+i*3], p, t[2+i*3]);
for (int i = 0;i<n;i++) fsub(t[2+i*3], t[0+i*3], t[2+i*3]);
for (int i = 0;i<n;i++) fmul(t[2+i*3], t[2+i*3], log2);
for (int i = 0;i<n;i++) {
movprfx(t[0+i*3], p, E);
fmad(t[0+i*3], p, t[2+i*3], D);
}
for (int i = 0;i<n;i++) fmad(t[0+i*3], p, t[2+i*3], C);
for (int i = 0;i<n;i++) fmad(t[0+i*3], p, t[2+i*3], B);
for (int i = 0;i<n;i++) fmad(t[0+i*3], p, t[2+i*3], one);
for (int i = 0;i<n;i++) fmad(t[0+i*3], p, t[2+i*3], one);
for (int i = 0;i<n;i++) fscale(t[0+i*3], p, t[1+i*3]);
19 / 27
• float x[16384];に対するstd::exp(float)との比較(nsec)
• FX700
• A64FXはXeonに比べてレイテンシが大きい
• https://github.com/fujitsu/A64FX
• frintn, fmul, fadd; 9clk
• fdiv, 98clk
• ループアンロールの効果が大きい
ベンチマーク
std::exp fmath::exp, N=1 N=2 N=3
217.3 19.0 11.8 8.4
20 / 27
• pow(2, i/64) (i=0,...,63)のテーブル引き命令
• floatのbit表現
• u=|s:1|e:8|f:23|, 𝑓 = −1 𝑠
2 𝑒−127
(1 +
𝑓
224), e:指数部, f:仮数部
• tbl[i] := 「pow(2, i/64)」の下位23bit
• 𝑥 = 2
𝑖
64 for 𝑖 = 0, … , 63は1 ≤ 𝑥 < 2なので常にx.e=127
• fexp(x)の挙動
SVEのfexpa
x.s x.e x.f
v=6bity=8bit
tbl[]
tbl[v]y=8bit0
21 / 27
• 2ベキの近似なのでそれにもっていく
• exp 𝑥 = 2 𝑥 log2 𝑒
= 2 𝑦
where 𝑦 ≔ 𝑥 log2 𝑒
• 𝑦 = 𝑛 + 𝑎 where 𝑛 ≔ floor(𝑦), 𝑎 ≔ 𝑛 − 𝑦, 0 ≤ 𝑎 < 1
• fexpaの添え字vが2ベキになるには[1, 2)にある必要性
• 𝑏 ≔ 1 + 𝑎とすると1 ≤ 𝑏 < 2, 𝑦 = 𝑛 − 1 + 𝑏
• 𝑏.f上位6bit vを取り出して使う c = fexpa(b.f >> 17)
• 𝑏 = 𝑏. f/224=v /26 + w/224, 𝑧 ≔ w/224
• 2 𝑏 =fexpa(v)⋅ 2 𝑧, 2 𝑧 = 𝑒log 2 𝑧 = 1 + log 2 𝑧 + log 2 𝑧 2/2
• 差分が小さいので2次の項まででよい
• https://github.com/herumi/misc/blob/master/sve/fmath-sve.hpp
fexpaの使い方
𝑏.f
v=6bit w=17bit
22 / 27
• 主要コード概要
fexpaによるexp
fmul(t0, t0, para.log2_e);
frintm(t1, p, t0); // floor : float -> float
fcvtzs(t2, p, t1); // n = float -> int
fsub(t1, t0, t1); // a
fadd(t1, t1, one); // b = 1 + a
lsr(t3, t1, 17); // v = b >> 17
fexpa(t3, t3); // c = fexpa(v)
fscale(t3, p, t2); // t3 *= 2^n
and_(t2.d, t1.d, not_mask17.d);
fsub(t2, t1, t2); // z
movprfx(t0, p, coeff2);
fmad(t0, p, t2, coeff1);
fmad(t0, p, t2, one); // 1 + log2 z + (log2)^2/2 z^2
fmul(t0, t3, t0);
23 / 27
• float x[16384];に対するstd::exp(float)との比較(nsec)
• ()内はfxpaを使わないバージョン
• 気持ち速いかも?
• 定数レジスタの個数 7 vs 5
• 中間レジスタの個数 3 vs 4
• 雑感
• 最初の方法に比べてレジスタの依存関係が複雑になる
• 定数レジスタが少ないのはよいがそこまでのメリットが
• もう少し精度があれば
• うまく速くなるレジスタ割り当てに試行錯誤(全数探索?)
ベンチマーク
std::exp N=1 N=2 N=3
217.3 18.2
(19.0)
11.3
(11.8)
8.4
(8.4)
24 / 27
• fdivの代わりに逆数近似命令(rcp)のあと補正する
• AVX-512
• SVE ; frecps(dst, src1, src2) ; // dst = 2 - src1 * src2
• 2回補正するとfloatに近い精度
逆数近似計算
t = rcp(x)
1/x = 2 * t - x t^2
// x : input/output
// t : temporary
// two : 2.0f
vrcp14ps(t, x);
vfnmadd213ps(x, t, two); // x = -xt + 2
vmulps(x, x, t);
25 / 27
• frintmあるいはaddの後のz0の逆数処理
• https://github.com/herumi/misc/blob/master/sve/inv.cpp
• ○で囲った部分が変?
• レジスタリネーミング?
逆数近似計算
// input : z0 (compute it with z0, z1, z2)
1. fdivr(z0.s, p0, one.s);
2. frecpe(z1.s, z0.s); frecps(z2.s, z0.s, z1.s);
fmul(z0.s, z1.s, z2.s);
3. frecpe(z1.s, z0.s); frecps(z3.s, z0.s, z1.s);
fmul(z0.s, z1.s, z3.s);
4. frecpe(z1.s, z0.s); frecps(z2.s, z0.s, z1.s);
fmul(z1.s, z1.s, z2.s);
frecps(z2.s, z0.s, z1.s); fmul(z0.s, z1.s, z2.s);
5. frecpe(z1.s, z0.s); frecps(z3.s, z0.s, z1.s);
fmul(z1.s, z1.s, z3.s);
frecps(z3.s, z0.s, z1.s); fmul(z0.s, z1.s, z3.s);
clk
frintm add
100 100
33 7.9
10.9 7.9
51 11.0
11.2 11.0
26 / 27
• AVX-512とSVE(A64FX)
• どちらも512-bitレジスタx32
• SVEの方がレイテンシが大きい
• ループアンロール重要
• レジスタリネーミングに気をつける場合がある?
まとめ
27 / 27

More Related Content

What's hot

汎用性と高速性を目指したペアリング暗号ライブラリ mcl
汎用性と高速性を目指したペアリング暗号ライブラリ mcl汎用性と高速性を目指したペアリング暗号ライブラリ mcl
汎用性と高速性を目指したペアリング暗号ライブラリ mclMITSUNARI Shigeo
 
Wavelet matrix implementation
Wavelet matrix implementationWavelet matrix implementation
Wavelet matrix implementationMITSUNARI Shigeo
 
BLS署名の実装とその応用
BLS署名の実装とその応用BLS署名の実装とその応用
BLS署名の実装とその応用MITSUNARI Shigeo
 
Haswellサーベイと有限体クラスの紹介
Haswellサーベイと有限体クラスの紹介Haswellサーベイと有限体クラスの紹介
Haswellサーベイと有限体クラスの紹介MITSUNARI Shigeo
 
高速な暗号実装のためにしてきたこと
高速な暗号実装のためにしてきたこと高速な暗号実装のためにしてきたこと
高速な暗号実装のためにしてきたことMITSUNARI Shigeo
 
準同型暗号の実装とMontgomery, Karatsuba, FFT の性能
準同型暗号の実装とMontgomery, Karatsuba, FFT の性能準同型暗号の実装とMontgomery, Karatsuba, FFT の性能
準同型暗号の実装とMontgomery, Karatsuba, FFT の性能MITSUNARI Shigeo
 
Spectre/Meltdownとその派生
Spectre/Meltdownとその派生Spectre/Meltdownとその派生
Spectre/Meltdownとその派生MITSUNARI Shigeo
 
SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介MITSUNARI Shigeo
 
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)MITSUNARI Shigeo
 
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)Takeshi Yamamuro
 
きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回Tomoya Kawanishi
 
研究動向から考えるx86/x64最適化手法
研究動向から考えるx86/x64最適化手法研究動向から考えるx86/x64最適化手法
研究動向から考えるx86/x64最適化手法Takeshi Yamamuro
 

What's hot (20)

汎用性と高速性を目指したペアリング暗号ライブラリ mcl
汎用性と高速性を目指したペアリング暗号ライブラリ mcl汎用性と高速性を目指したペアリング暗号ライブラリ mcl
汎用性と高速性を目指したペアリング暗号ライブラリ mcl
 
Prosym2012
Prosym2012Prosym2012
Prosym2012
 
フラグを愛でる
フラグを愛でるフラグを愛でる
フラグを愛でる
 
Wavelet matrix implementation
Wavelet matrix implementationWavelet matrix implementation
Wavelet matrix implementation
 
BLS署名の実装とその応用
BLS署名の実装とその応用BLS署名の実装とその応用
BLS署名の実装とその応用
 
LLVM最適化のこつ
LLVM最適化のこつLLVM最適化のこつ
LLVM最適化のこつ
 
emcjp Item 42
emcjp Item 42emcjp Item 42
emcjp Item 42
 
Haswellサーベイと有限体クラスの紹介
Haswellサーベイと有限体クラスの紹介Haswellサーベイと有限体クラスの紹介
Haswellサーベイと有限体クラスの紹介
 
高速な暗号実装のためにしてきたこと
高速な暗号実装のためにしてきたこと高速な暗号実装のためにしてきたこと
高速な暗号実装のためにしてきたこと
 
準同型暗号の実装とMontgomery, Karatsuba, FFT の性能
準同型暗号の実装とMontgomery, Karatsuba, FFT の性能準同型暗号の実装とMontgomery, Karatsuba, FFT の性能
準同型暗号の実装とMontgomery, Karatsuba, FFT の性能
 
ゆるバグ
ゆるバグゆるバグ
ゆるバグ
 
Spectre/Meltdownとその派生
Spectre/Meltdownとその派生Spectre/Meltdownとその派生
Spectre/Meltdownとその派生
 
SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介
 
llvm入門
llvm入門llvm入門
llvm入門
 
optimal Ate pairing
optimal Ate pairingoptimal Ate pairing
optimal Ate pairing
 
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)
Xeon PhiとN体計算コーディング x86/x64最適化勉強会6(@k_nitadoriさんの代理アップ)
 
From IA-32 to avx-512
From IA-32 to avx-512From IA-32 to avx-512
From IA-32 to avx-512
 
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
 
きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回
 
研究動向から考えるx86/x64最適化手法
研究動向から考えるx86/x64最適化手法研究動向から考えるx86/x64最適化手法
研究動向から考えるx86/x64最適化手法
 

Similar to HPC Phys-20201203

x86とコンテキストスイッチ
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチMasami Ichikawa
 
Rのデータ構造とメモリ管理
Rのデータ構造とメモリ管理Rのデータ構造とメモリ管理
Rのデータ構造とメモリ管理Takeshi Arabiki
 
Fftw誰得ガイド
Fftw誰得ガイドFftw誰得ガイド
Fftw誰得ガイドchunjp
 
8 並列計算に向けた pcセッティング
8 並列計算に向けた pcセッティング8 並列計算に向けた pcセッティング
8 並列計算に向けた pcセッティングTakeshi Takaishi
 
Lisp tutorial for Pythonista : Day 1
Lisp tutorial for Pythonista : Day 1Lisp tutorial for Pythonista : Day 1
Lisp tutorial for Pythonista : Day 1Ransui Iso
 
PBL1-v1-007j.pptx
PBL1-v1-007j.pptxPBL1-v1-007j.pptx
PBL1-v1-007j.pptxNAIST
 
多次元配列の効率的利用法の検討
多次元配列の効率的利用法の検討多次元配列の効率的利用法の検討
多次元配列の効率的利用法の検討Yu Sato
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門Hideyuki Tanaka
 
F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~Nobuhisa Koizumi
 
PFDS 8.4.3 Real-Time Deques
PFDS 8.4.3 Real-Time DequesPFDS 8.4.3 Real-Time Deques
PFDS 8.4.3 Real-Time Deques昌平 村山
 
PBL1-v1-006j.pptx
PBL1-v1-006j.pptxPBL1-v1-006j.pptx
PBL1-v1-006j.pptxNAIST
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Ransui Iso
 
kagamicomput201707
kagamicomput201707kagamicomput201707
kagamicomput201707swkagami
 
OpenFlowで覚えるネットワーク
OpenFlowで覚えるネットワークOpenFlowで覚えるネットワーク
OpenFlowで覚えるネットワークM Hagiwara
 

Similar to HPC Phys-20201203 (20)

x86とコンテキストスイッチ
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチ
 
What is Metasepi?
What is Metasepi?What is Metasepi?
What is Metasepi?
 
Rのデータ構造とメモリ管理
Rのデータ構造とメモリ管理Rのデータ構造とメモリ管理
Rのデータ構造とメモリ管理
 
Rの高速化
Rの高速化Rの高速化
Rの高速化
 
Fftw誰得ガイド
Fftw誰得ガイドFftw誰得ガイド
Fftw誰得ガイド
 
Boost.SIMD
Boost.SIMDBoost.SIMD
Boost.SIMD
 
8 並列計算に向けた pcセッティング
8 並列計算に向けた pcセッティング8 並列計算に向けた pcセッティング
8 並列計算に向けた pcセッティング
 
Lisp tutorial for Pythonista : Day 1
Lisp tutorial for Pythonista : Day 1Lisp tutorial for Pythonista : Day 1
Lisp tutorial for Pythonista : Day 1
 
PBL1-v1-007j.pptx
PBL1-v1-007j.pptxPBL1-v1-007j.pptx
PBL1-v1-007j.pptx
 
多次元配列の効率的利用法の検討
多次元配列の効率的利用法の検討多次元配列の効率的利用法の検討
多次元配列の効率的利用法の検討
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
PFI Seminar 2010/02/18
PFI Seminar 2010/02/18PFI Seminar 2010/02/18
PFI Seminar 2010/02/18
 
F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~F#入門 ~関数プログラミングとは何か~
F#入門 ~関数プログラミングとは何か~
 
PFDS 8.4.3 Real-Time Deques
PFDS 8.4.3 Real-Time DequesPFDS 8.4.3 Real-Time Deques
PFDS 8.4.3 Real-Time Deques
 
PBL1-v1-006j.pptx
PBL1-v1-006j.pptxPBL1-v1-006j.pptx
PBL1-v1-006j.pptx
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3
 
V6 unix in okinawa
V6 unix in okinawaV6 unix in okinawa
V6 unix in okinawa
 
Gnuplotあれこれ
GnuplotあれこれGnuplotあれこれ
Gnuplotあれこれ
 
kagamicomput201707
kagamicomput201707kagamicomput201707
kagamicomput201707
 
OpenFlowで覚えるネットワーク
OpenFlowで覚えるネットワークOpenFlowで覚えるネットワーク
OpenFlowで覚えるネットワーク
 

More from MITSUNARI Shigeo

暗号技術の実装と数学
暗号技術の実装と数学暗号技術の実装と数学
暗号技術の実装と数学MITSUNARI Shigeo
 
範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコル範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコルMITSUNARI Shigeo
 
暗認本読書会13 advanced
暗認本読書会13 advanced暗認本読書会13 advanced
暗認本読書会13 advancedMITSUNARI Shigeo
 
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法MITSUNARI Shigeo
 
WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装MITSUNARI Shigeo
 
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化MITSUNARI Shigeo
 
LazyFP vulnerabilityの紹介
LazyFP vulnerabilityの紹介LazyFP vulnerabilityの紹介
LazyFP vulnerabilityの紹介MITSUNARI Shigeo
 
暗号化したまま計算できる暗号技術とOSS開発による広がり
暗号化したまま計算できる暗号技術とOSS開発による広がり暗号化したまま計算できる暗号技術とOSS開発による広がり
暗号化したまま計算できる暗号技術とOSS開発による広がりMITSUNARI Shigeo
 

More from MITSUNARI Shigeo (20)

暗号技術の実装と数学
暗号技術の実装と数学暗号技術の実装と数学
暗号技術の実装と数学
 
範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコル範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコル
 
暗認本読書会13 advanced
暗認本読書会13 advanced暗認本読書会13 advanced
暗認本読書会13 advanced
 
暗認本読書会12
暗認本読書会12暗認本読書会12
暗認本読書会12
 
暗認本読書会11
暗認本読書会11暗認本読書会11
暗認本読書会11
 
暗認本読書会10
暗認本読書会10暗認本読書会10
暗認本読書会10
 
暗認本読書会9
暗認本読書会9暗認本読書会9
暗認本読書会9
 
暗認本読書会8
暗認本読書会8暗認本読書会8
暗認本読書会8
 
暗認本読書会7
暗認本読書会7暗認本読書会7
暗認本読書会7
 
暗認本読書会6
暗認本読書会6暗認本読書会6
暗認本読書会6
 
暗認本読書会5
暗認本読書会5暗認本読書会5
暗認本読書会5
 
暗認本読書会4
暗認本読書会4暗認本読書会4
暗認本読書会4
 
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
 
私とOSSの25年
私とOSSの25年私とOSSの25年
私とOSSの25年
 
WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装
 
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
 
楕円曲線と暗号
楕円曲線と暗号楕円曲線と暗号
楕円曲線と暗号
 
LazyFP vulnerabilityの紹介
LazyFP vulnerabilityの紹介LazyFP vulnerabilityの紹介
LazyFP vulnerabilityの紹介
 
集約署名
集約署名集約署名
集約署名
 
暗号化したまま計算できる暗号技術とOSS開発による広がり
暗号化したまま計算できる暗号技術とOSS開発による広がり暗号化したまま計算できる暗号技術とOSS開発による広がり
暗号化したまま計算できる暗号技術とOSS開発による広がり
 

Recently uploaded

新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
プレイマットのパターン生成支援ツールの評価
プレイマットのパターン生成支援ツールの評価プレイマットのパターン生成支援ツールの評価
プレイマットのパターン生成支援ツールの評価sugiuralab
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directoryosamut
 
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxAtomu Hidaka
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000Shota Ito
 
プレイマットのパターン生成支援ツール
プレイマットのパターン生成支援ツールプレイマットのパターン生成支援ツール
プレイマットのパターン生成支援ツールsugiuralab
 

Recently uploaded (7)

新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
 
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
 
プレイマットのパターン生成支援ツールの評価
プレイマットのパターン生成支援ツールの評価プレイマットのパターン生成支援ツールの評価
プレイマットのパターン生成支援ツールの評価
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory
 
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000
 
プレイマットのパターン生成支援ツール
プレイマットのパターン生成支援ツールプレイマットのパターン生成支援ツール
プレイマットのパターン生成支援ツール
 

HPC Phys-20201203

  • 2. • expの近似計算 • AVX-512による実装 • SVEによる実装 • レジスタリネーミング • SVEのfexpaを使った近似計算 • 実装 • 逆数近似命令 目次 2 / 27
  • 3. • 級数展開を使う • 𝑒 𝑥 = 1 + 𝑥 + 𝑥2 2 + 𝑥3 6 + ⋯ • 要請 • 級数展開における𝑥は小さいのが望ましい • 2 𝑛(𝑛が整数)は高速に求められる • 式変形 • 𝑒 𝑥 = 2 𝑥 log2 𝑒 • 𝑦 ≔ 𝑥 log2 𝑒 を整数部分𝑛と小数部分𝑎に分ける( 𝑎 ≤ 1/2) • 𝑦 = 𝑛 + 𝑎 • 𝑒 𝑥 = 2 𝑦 = 2 𝑛+𝑎 = 2 𝑛2 𝑎 • 2 𝑎 = 𝑒 𝑎 log(2) • 𝑏 ≔ 𝑎 log(2), 𝑏 ≤ log 2 /2 = 0.346 exp(float x);の近似計算(1/3) 3 / 27
  • 4. • floatなら1e-6程度まで求まれば十分 • 5次の項まで計算 • 𝑒 𝑥 = 1 + 𝑥 + 𝑥2 2 + 𝑥3 6 + 𝑥4 24 + 𝑥5 120 • 係数補正 • 𝑓 𝑥 ≔ 1 + 𝑥 + 𝐵𝑥 + 𝐶𝑥2 + 𝐷𝑥3 + 𝐸𝑥4 + 𝐹𝑥5 • 𝑥 ∈ [−𝐿, 𝐿] where 𝐿 ≔ log(2)/2 • 𝐼 ≔ ‫׬‬−𝐿 𝐿 𝑓 𝑥 − 𝑒 𝑥 2 𝑑𝑥を最小化する(𝐵, 𝐶, 𝐷, 𝐸, 𝐹)を求める • 1次の項(𝐴)を1に固定しているのは0次と共用したいから • 誤差が概ね半分程度になる • Sollyaのremezよりは誤差が小さい? • L2ノルムとL1ノルムどちらがよいかアプリ依存? exp(float x);の近似計算(2/3) 4 / 27
  • 5. • まとめ • 配列に対する一括処理 • テーブルを使わないためSIMD化しやすい exp(float x);の近似計算(3/3) input : x x = x * log_2(e) n = round(x) ; 四捨五入 a = x - n b = a * log(2) c = 1 + b(1 + b(B + b(C + b(D + E b)))) y = c * 2^n output : y void expv(float *dst, const float *src, size_t n) { for (size_t i = 0; i < n; i++) dst[i] = exp(src[i]); } 5 / 27
  • 6. • Intelの512bit幅のSIMD命令セット • 32個の512bit AVXレジスタ • double x 8, float x 16, int32 x 16, int16 x 32, int8 x 64などの型 • 概ね3オペランド • op(r1, r2, r3) ; r1 = r2 op r3 • d ; 32-bit int, ps ; float, pd ; double • 積和演算FMA • d = a * b + cが望ましいが4オペランドになるので AVX-512 paddd zmm0, zmm1, zmm2 // zmm0 = zmm1 + zmm2 as 32-bit int addps zmm0, zmm1, zmm2 // as float addpd zmm0, zmm1, zmm2 // as double vfmadd132ps r1, r2, r3 // r1 = r1 * r3 + r2 vfmadd213ps r1, r2, r3 // r1 = r2 * r1 + r3 vfmadd231ps r1, r2, r3 // r1 = r2 * r3 + r1 6 / 27
  • 7. • C++でCPUのニーモニックを記述 • 実行時にそれに対応する機械語が生成されて実行する • 例 : 「整数nを足す関数」を生成する関数 • genAdd(5); // 「5を足す関数」が実行時に生成される • 生成された関数 • exp自体は静的な関数でよいがIntel oneDNNではパーツの一つ として実行時生成されている Xbyak/Xbyak_aarch64 struct Code : Xbyak::CodeGenerator { void genAdd(int n) { // rsiはLinuxでの関数の第一引数 lea(rax, ptr[rsi + n]); ret(); } }; lea rax, [rsi + 5] // + 5は即値 ret 7 / 27
  • 8. • round関数 • vrndscaleps(dst, src, round_ctl) • round_ctl ; 2bitフラグ • 00 ; nearest even ; 一番近い偶数丸め • 01 ; equal or smaller ; 切り捨て • 10 ; equal or larger ; 切り上げ • 11 ; truncate ; 0方向に切り捨て • vrndscaleps(dst, src, 0) ; dst = round(src); • c = 1 + b(1 + b(B + b(C + b(D + E b)))) • FMAを5回適用 • 𝑦 = 𝑐 × 2 𝑛の部分 • AVX2までは整数𝑛をビットシフトしてfloatに変換して... • AVX-512ではvscalefps(dst, r1, r2) // dst = r1 * 2^r2 AVX-512によるexp 8 / 27
  • 9. • 準備 • 下記変数はzmmレジスタのエリアス名 • log2_e, log2, one, B, C, D, Eは事前に定数設定 • t1, t2は一時レジスタ, xが入出力(x = exp(x)) exp一つ分 genExpOne() { vmulps(x, log2_e); // x *= log2_e vrndscaleps(t1, x, 0); // t1 = round(x) vsubps(x, t1); // x = x - t1 ; 小数部分a vmulps(x, log2); // a * log2 vmovaps(t2, E); vfmadd213ps(t2, x, D); vfmadd213ps(t2, x, C); vfmadd213ps(t2, x, B); vfmadd213ps(t2, x, one); vfmadd213ps(t2, x, one); // t2 = 1+b(1+b(B+b(C+b(D+E b)))) vscalefps(x, t2, t1); // x = t2 * 2^t1 } 9 / 27
  • 10. • ループnが16の倍数でないときの扱い • マスクレジスタk1, ..., k7 • zmmレジスタのkに対応するbitが1なら処理, 0なら非処理 • T_zを指定すると非処理の部分に0が入る • vmovups(zm0|k1|T_z, ptr[src]); • readしない部分はメモリアクセスしない(アクセス違反しない) • マスクレジスタを指定しないときに比べてやや遅い • 16の倍数のときは指定しない方がよい 端数処理(1/2) k1 = 0b00..01111111 (8bitの1)のとき src | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9| a| b| ... |← readしない → zm0 | x0| x1| x2| x3| x4| x5| x6| x7| 0| | 0| 0|... 10 / 27
  • 11. • コア部分 端数処理(2/2) Label lp = L(); // メインループ vmovups(zm0, ptr[src]); // zm0 = *src add(src, 64); // src += 64 genExpOne() // zm0 = exp(zm0) vmovups(ptr[dst], zm0); // *dst = zm0 add(dst, 64); // dst += 64 sub(n, 16); // n -= 16 jnz(lp); // if (n != 0) goto lp L(mod16); // 端数処理 and_(ecx, 15); // n &= 15 jz(exit); mov(eax, 1); shl(eax, cl); // eax = 1 << n sub(eax, 1); // eax = (1 << n) - 1 kmovd(k1, eax); // k1 = nビットの1 vmovups(zm0|k1|T_z, ptr[src]); // 残り読み genExpOne() // zm0 = exp(zm0) vmovups(ptr[dst]|k1, zm0|k1); // 残り書き L(exit); 11 / 27
  • 12. • float x[16384];に対するstd::exp(float)との比較(clk) • Xeon Platinum 8280 2.7GHz, g++ 9.3.0 Ubuntu 20.04.1 LTS • genOneExpをループアンロール • fmath::exp 8.7clk→7.2clk • https://github.com/herumi/fmath/blob/master/fmath2.hpp ベンチマーク std::exp fmath::exp 140.1 8.7 genExpTwo() { vmulps(x0, log2_e); vmulps(x3, log2_e); vrndscaleps(t1, x0, 0); vrndscaleps(t4, x1, 0); vsubps(x0, t1); vsubps(x3, t4); ... 12 / 27
  • 13. • Armの可変長SIMD命令セット • 128~1024(?)bitレジスタ • double x 8, float x 16, int32 x 16, int16 x 32, int8 x 64などの型 • 概ね3オペランド • A64FX(富岳のCPU)では32個の512bitレジスタz0, ..., z31 • movprfx(dstをsrcとして利用) • predは述語レジスタ • マスクレジスタ相当 • AVX-512と異なり常に指定 SVE fadd z0.s, z1.s, z2.s // as float add z0.b, z1.b, z2.b // as int8 movprfx(dst, pred, r1); fmadd(dst, pred, r2, r3); // dst = r1 * r2 + r3 13 / 27
  • 14. • SVEレジスタの各要素を処理する(1)か否(0)かを指定 • 例 ld1w(z.s, p/T_z, ptr(src)); • z = *src; // float x 16個読み込み • i番目の各要素(i = 0, ..., 15)について • 述語レジスタp[i] = 1ならz[i] = src1[i] • p[i] = 0ならT_z(zero)を指定しているのでz[i] = 0 • T_zを指定しなければp[i]の値を変更しない 述語レジスタ src x0 x1 x2 x3... z.s x0 0 x2 x3... p 1 0 1 1 14 / 27
  • 15. • 「x4 + i < n」が成り立つ添え字までp[i] = 1にする • 例 x4 + 16 <= nならi = 0, ..., 15についてp[i] = 1 • 全てのデータが有効 • x4 + 3 = nならi ≦ 2についてp[i] = 1, その他p[i] = 0 • p[i] = 0の部分はデータを読まない・書かない • 読み書き属性が無い領域でも大丈夫 whilelt(p.s, x4, n); i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 p 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 p 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 15 / 27
  • 16. • メイン部分 • x4 + 16 ≦ nである限りp0[i] = 1 for i = 0, ..., 15 • ループの最終ではp0[i] = 1 for i <(n % 16), p0[i] = 0(otherwise) • メリット • SVEが256bitや1024bitでも同じコードで動く • SVEはScalable Vector Extensionの略 • デメリット • あまり速くない ; 結局AVX-512のように分けた方がよい ループの終わり処理 Label lp = L(); ld1w(z0.s, p0/T_z, ptr(src1, x4, LSL, 2));// z0 = src1[x4 << 2] ... incd(x4); // x4 += 16 L(cond); whilelt(p0.s, x4, n); // while (x4 < n)なら b_first(lp); // lpラベルにジャンプ 16 / 27
  • 17. • AVX-512とほぼ同じ • 述語レジスタpはall 1に設定 • round関数はfrintn • fscale(x, p, n)のnがfloatではなくintなのでfcvtzsでintに変換 sveによるgenExpOne fmul(tz0, tz0, log2_e); frintn(tz2, p, tz0); // round : float -> float fcvtzs(tz1, p, tz2); // float -> int fsub(tz2, tz0, tz2); fmul(tz2, tz2, log2); movprfx(tz0, p, E); fmad(tz0, p, tz2, D); // tz0 = tz2 * E + D fmad(tz0, p, tz2, C); fmad(tz0, p, tz2, B); fmad(tz0, p, tz2, one); fmad(tz0, p, tz2, one); fscale(tz0, p, tz1); // tz0 *= 2^tz1 17 / 27
  • 18. • frintnなどの命令のあとの依存関係 • レジスタ名を入れ換えると2倍速くなるケース(74→32nsec) • https://github.com/herumi/misc/commit/0362d5647f693be66 da841eea7ca333d0f5b5329 レジスタリネーミングが苦手? 18 / 27
  • 19. • 利用レジスタを増やしてループ展開 ループアンロール // n = 1 or 2 or 3 for (int i = 0;i<n;i++) fmul(t[0+i*3], t[0+i*3], log2_e); for (int i = 0;i<n;i++) frintn(t[2+i*3], p, t[0+i*3]); for (int i = 0;i<n;i++) fcvtzs(t[1+i*3], p, t[2+i*3]); for (int i = 0;i<n;i++) fsub(t[2+i*3], t[0+i*3], t[2+i*3]); for (int i = 0;i<n;i++) fmul(t[2+i*3], t[2+i*3], log2); for (int i = 0;i<n;i++) { movprfx(t[0+i*3], p, E); fmad(t[0+i*3], p, t[2+i*3], D); } for (int i = 0;i<n;i++) fmad(t[0+i*3], p, t[2+i*3], C); for (int i = 0;i<n;i++) fmad(t[0+i*3], p, t[2+i*3], B); for (int i = 0;i<n;i++) fmad(t[0+i*3], p, t[2+i*3], one); for (int i = 0;i<n;i++) fmad(t[0+i*3], p, t[2+i*3], one); for (int i = 0;i<n;i++) fscale(t[0+i*3], p, t[1+i*3]); 19 / 27
  • 20. • float x[16384];に対するstd::exp(float)との比較(nsec) • FX700 • A64FXはXeonに比べてレイテンシが大きい • https://github.com/fujitsu/A64FX • frintn, fmul, fadd; 9clk • fdiv, 98clk • ループアンロールの効果が大きい ベンチマーク std::exp fmath::exp, N=1 N=2 N=3 217.3 19.0 11.8 8.4 20 / 27
  • 21. • pow(2, i/64) (i=0,...,63)のテーブル引き命令 • floatのbit表現 • u=|s:1|e:8|f:23|, 𝑓 = −1 𝑠 2 𝑒−127 (1 + 𝑓 224), e:指数部, f:仮数部 • tbl[i] := 「pow(2, i/64)」の下位23bit • 𝑥 = 2 𝑖 64 for 𝑖 = 0, … , 63は1 ≤ 𝑥 < 2なので常にx.e=127 • fexp(x)の挙動 SVEのfexpa x.s x.e x.f v=6bity=8bit tbl[] tbl[v]y=8bit0 21 / 27
  • 22. • 2ベキの近似なのでそれにもっていく • exp 𝑥 = 2 𝑥 log2 𝑒 = 2 𝑦 where 𝑦 ≔ 𝑥 log2 𝑒 • 𝑦 = 𝑛 + 𝑎 where 𝑛 ≔ floor(𝑦), 𝑎 ≔ 𝑛 − 𝑦, 0 ≤ 𝑎 < 1 • fexpaの添え字vが2ベキになるには[1, 2)にある必要性 • 𝑏 ≔ 1 + 𝑎とすると1 ≤ 𝑏 < 2, 𝑦 = 𝑛 − 1 + 𝑏 • 𝑏.f上位6bit vを取り出して使う c = fexpa(b.f >> 17) • 𝑏 = 𝑏. f/224=v /26 + w/224, 𝑧 ≔ w/224 • 2 𝑏 =fexpa(v)⋅ 2 𝑧, 2 𝑧 = 𝑒log 2 𝑧 = 1 + log 2 𝑧 + log 2 𝑧 2/2 • 差分が小さいので2次の項まででよい • https://github.com/herumi/misc/blob/master/sve/fmath-sve.hpp fexpaの使い方 𝑏.f v=6bit w=17bit 22 / 27
  • 23. • 主要コード概要 fexpaによるexp fmul(t0, t0, para.log2_e); frintm(t1, p, t0); // floor : float -> float fcvtzs(t2, p, t1); // n = float -> int fsub(t1, t0, t1); // a fadd(t1, t1, one); // b = 1 + a lsr(t3, t1, 17); // v = b >> 17 fexpa(t3, t3); // c = fexpa(v) fscale(t3, p, t2); // t3 *= 2^n and_(t2.d, t1.d, not_mask17.d); fsub(t2, t1, t2); // z movprfx(t0, p, coeff2); fmad(t0, p, t2, coeff1); fmad(t0, p, t2, one); // 1 + log2 z + (log2)^2/2 z^2 fmul(t0, t3, t0); 23 / 27
  • 24. • float x[16384];に対するstd::exp(float)との比較(nsec) • ()内はfxpaを使わないバージョン • 気持ち速いかも? • 定数レジスタの個数 7 vs 5 • 中間レジスタの個数 3 vs 4 • 雑感 • 最初の方法に比べてレジスタの依存関係が複雑になる • 定数レジスタが少ないのはよいがそこまでのメリットが • もう少し精度があれば • うまく速くなるレジスタ割り当てに試行錯誤(全数探索?) ベンチマーク std::exp N=1 N=2 N=3 217.3 18.2 (19.0) 11.3 (11.8) 8.4 (8.4) 24 / 27
  • 25. • fdivの代わりに逆数近似命令(rcp)のあと補正する • AVX-512 • SVE ; frecps(dst, src1, src2) ; // dst = 2 - src1 * src2 • 2回補正するとfloatに近い精度 逆数近似計算 t = rcp(x) 1/x = 2 * t - x t^2 // x : input/output // t : temporary // two : 2.0f vrcp14ps(t, x); vfnmadd213ps(x, t, two); // x = -xt + 2 vmulps(x, x, t); 25 / 27
  • 26. • frintmあるいはaddの後のz0の逆数処理 • https://github.com/herumi/misc/blob/master/sve/inv.cpp • ○で囲った部分が変? • レジスタリネーミング? 逆数近似計算 // input : z0 (compute it with z0, z1, z2) 1. fdivr(z0.s, p0, one.s); 2. frecpe(z1.s, z0.s); frecps(z2.s, z0.s, z1.s); fmul(z0.s, z1.s, z2.s); 3. frecpe(z1.s, z0.s); frecps(z3.s, z0.s, z1.s); fmul(z0.s, z1.s, z3.s); 4. frecpe(z1.s, z0.s); frecps(z2.s, z0.s, z1.s); fmul(z1.s, z1.s, z2.s); frecps(z2.s, z0.s, z1.s); fmul(z0.s, z1.s, z2.s); 5. frecpe(z1.s, z0.s); frecps(z3.s, z0.s, z1.s); fmul(z1.s, z1.s, z3.s); frecps(z3.s, z0.s, z1.s); fmul(z0.s, z1.s, z3.s); clk frintm add 100 100 33 7.9 10.9 7.9 51 11.0 11.2 11.0 26 / 27
  • 27. • AVX-512とSVE(A64FX) • どちらも512-bitレジスタx32 • SVEの方がレイテンシが大きい • ループアンロール重要 • レジスタリネーミングに気をつける場合がある? まとめ 27 / 27