Xbyakの紹介とその周辺

MITSUNARI Shigeo
MITSUNARI ShigeoSoftware Engineer em Cybozu Labs, Inc
Xbyakの紹介とその周辺
カーネル/VM探検隊@関西 9回目 2018/9/22
光成滋生
• @herumi
• https://github.com/herumi/she-wasm
• ペアリングベースの準同型暗号のWebAssembly向け実装
• IEEE trans. on Computers 2014, AsiaCCS 2018など
• https://github.com/herumi/bls
• BLS署名の実装
• Dfinityというブロックチェーン系ベンチャーが使ってる
自己紹介
2 / 45
• C++用のx86/x64専用JITアセンブラ
• https://github.com/herumi/xbyak
• 自分が使いたい(暗号用に開発を始めた)アセンブラ
• 開発を初めてもうすぐ12年目
• AVX-512フルサポート
• ちなみに現在(2018/9)Intelの一番長い名前の命令は
• vgf2p8affineinvqb(17文字)
• Galois Field Affine Transformation Inverse
• 𝐹28の元𝑥に対して𝐴𝑥−1 + 𝐵を計算する
Xbyakって何?
3 / 45
• NASM, gasなどの通常の静的なアセンブラに比べて
• コードを書きやすい(個人の感想)
• C++との連係がしやすい(個人の...)
• VMを書きやすい(略)
• V8やWebkitなどのJavaScriptエンジンも同等の
JITアセンブラを持ってる(ARMなどにも対応してる)
従来のアセンブラとの比較
4 / 45
• 整数nを返す関数を生成するクラス
• インスタンスを作成し関数ポインタを取り出して実行
実行時に整数nを返す関数を生成
struct Code : Xbyak::CodeGenerator {
Code(int n) {
mov(eax, n); // 注意 インラインアセンブラではない
ret(); // 純粋なC++のコード
}
};
Code c1(3);
auto f = c1.getCode<int (*)()>();// intを返す関数ポインタ
printf("%d¥n", f()); // 3
実行時にコード生成
mov eax, 3
ret
5 / 45
• MASMに似せるための各種演算子オーバーロード
• Intel命令をそのまま書けるので直感的に操作しやすい
• 小さなブロックを組み合わせて作る感覚が楽しい
雰囲気
void gen_add(const RegExp& dst, const RegExp& src);
void f(int n) {
auto addr = n > 0 ? rsi + rax * 8 + n * 8 : rdi;
mov(rax, ptr[addr]);
vgatherqpd(zmm5 | k7, ptr [rax + 64 + zmm21 * 2]);
gen_add(rsp + 8, rsi + 8);
}
6 / 45
• template引数で長さを指定する整数クラス
• 最大の大きさを指定するNはコンパイル時指定
• 実際の整数の大きさを指定するnは実行時指定
• このようなNを外部アセンブラと連係するのは面倒
固定多倍長加算
template<size_t N>
struct Int {
void init(size_t n); // n * 64 bit整数として利用(n < N)
uint64_t d[N];
// z = x + y
void (*add)(Int& z, const Int& x, const Int& y);
};
7 / 45
• 64 * n-bit加算(ビット長に応じたコード生成)
多倍長加算の例
GenAdd(int n) {
for (int i = 0; i < n; i++) {
mov(rax, ptr [x+i*8]);
if (i == 0) add(rax, ptr [y+i*8]);
else adc(rax, ptr [y+i*8]);
mov(ptr [z+i*8], rax);
}
ret(); }
add3:
mov rax, [rsi]
add rax, [rdx]
mov [rdi], rax
mov rax, [rsi + 8]
adc rax, [rdx + 8]
mov [rdi + 8], rax
mov rax, [rsi + 16]
adc rax, [rdx + 16]
mov [rdi + 16], rax
ret
add2:
mov rax, [rsi]
add rax, [rdx]
mov [rdi], rax
mov rax, [rsi + 8]
adc rax, [rdx + 8]
mov[rdi + 8], eax
ret
N=2 N=3
8 / 45
• LLVMのbitコード(VMのコード)で書けば各種
CPUへの最適化コードが出力されるバラ色の世界
• と思っていたときもあった
• 意外と各種アーキテクチャに縛られる
• x64向けにはコード生成されるがARMではランタイムエラー
• WebAssemblyへもランタイムエラー
• 32bit/64bit CPU向けに別々にコードを書く必要(私の経験)
• VMとは
• 手書き最適化に比べて1~2割遅い(ことが多い)
• Intel専用命令使いたい・勝手に使われたくない
• もちろん開発コスト比を考慮すると○
LLVMのJITでええんじゃね?
9 / 45
• LLVMの例
• x64での128bit演算出力
• 命令順序を除いて先ほどのコードと同一コードを生成
• 1024bitなら?
128~1024bitの加算
define void @add128(i128* %pz, i128* %px, i128* %py)
{
%x = load i128, i128* %px
%y = load i128, i128* %py
%z = add i128 %x, %y
store i128 %z, i128* %pz
ret void
}
10 / 45
• 怒濤のレジスタスピル(溢れ)
• xとyを足してzに配置するとき
下位レジスタから順にやれば
データの退避は不要
• それを把握していないので
一度スタックにコピーしてる
• 無駄にSIMDを使って遅くなる
こともある
• 余談
• llcに-pre-RA-sched=list-ilp
-max-sched-reorder=16
でspillしなくなる(たまたま?)
• llc --help-hiddenすると大量の隠しオプションが現れる
• バージョン非互換なものも多い
1024bitの加算の出力
movq 16(%rsi), %r13
movq (%rsi), %rbx
movq 8(%rsi), %r15
movq 120(%rdx), %rax
movq %rax, -8(%rsp) # 8-byte Spill
movq 112(%rdx), %rax
movq 104(%rdx), %rcx
movq %rcx, -24(%rsp) # 8-byte Spill
movq 96(%rdx), %rcx
movq 88(%rdx), %rbp
movq %rbp, -32(%rsp) # 8-byte Spill
movq 80(%rdx), %r8
movq 72(%rdx), %r12
movq 64(%rdx), %r14
movq 56(%rdx), %rbp
addq (%rdx), %rbx
movq %rbx, -16(%rsp) # 8-byte Spill
...
11 / 45
• PS2エミュレータ PCSX2
• https://github.com/PCSX2/pcsx2 ; plugin/shaderまわり
• (当時)画像処理部分を画面のRGBのデータ並び(ビデオカ
ードごとに違ってたりした)に応じた最適なコードをtempate
を使ってコンパイル時コード生成
• バイナリコードの肥大化
• 実行時生成にすることでバイナリコードの削減と高速化
• mrubyのJIT by @miura1729さん
• https://github.com/miura1729/mruby/
• 解説記事
https://qiita.com/miura1729/items/a1828849ec8fec596e74
• マンデルブロートなどの計算主体なものはx10ぐらいらしい
利用例
12 / 45
• 正規表現JITエンジン by @sinya8282
• https://github.com/sinya8282/Regen
• JavaScript VMのJIT by @Constellation
• https://github.com/Constellation/iv/
• http://labs.cybozu.co.jp/youth.html
サイボウズ・ラボユース生によるJIT
13 / 45
• Citra(shaderのJIT部分に使ってるもよう)
• https://github.com/citra-emu/citra
• https://github.com/citra-
emu/citra/blob/master/src/video_core/shader/shader_jit_x64_
compiler.cpp
• log2やexp2なども実行時コード生成してる
3DSエミュレータ
SSE4.1が使えれば
それを利用
無ければ代替命令を
生成
14 / 45
• 機械学習・深層学習ライブラリ
• https://github.com/intel/mkl-dnn
• 最新CPUのAVX-512命令v4fmaddpsなども利用
• jit_avx512_common_conv_winograd_kernel_f32.cpp
• templateとlambdaとアセンブラの混在感が味わい深い?
Intel MKL-DNN
15 / 45
• 自動パフォーマンスチューニングワークショップの
Intelの招待講演
• http://iwapt.org/2018/iwapt2018_proceedings/SarahKnepper_j
it_compilation_iwapt.pdf
• ありがたいことにほとんどXbyakの使い方
iWAPT2018
16 / 45
• 直感的にJITコードを記述できる
• 前述のPDFから
行列演算の端数処理
生成コード
17 / 45
• CPUのL2, L3キャッシュサイズを取得
• https://github.com/intel/mkl-
dnn/blob/19588d1484911a3dc7933b32ce71d2f1b9bbbb78/sr
c/cpu/jit_avx512_core_fp32_wino_conv_2x3.cpp#L580
• データサイズやレジスタの個数を計算しながら適切な
ループサイズを計算してJITしてる模様(詳細は未読)
キャッシュサイズを意識
const int L2_cap
= get_cache_size(2, true) / sizeof(float);
const int L3_capacity
= get_cache_size(3, false) / sizeof(float);
18 / 45
• Intelによる小さい行列計算専用JITライブラリ
• https://github.com/hfp/libxsmm
• Cでゼロから作られてる(without Xbyak)
• https://www.ixpug.org/images/docs/IXPUG_Annual_Spring_Co
nference_2018/09-PABST-Libxsmm.pdf
LIBXSMM
19 / 45
• メモリを確保
• malloc/_aligned_malloc/posix_memalign
• 命令フォーマットにしたがってバイトコードを展開
• modRM, SIB, rex, vex, evex, etc., AVX-512は結構大変
• exec属性を付与して実行
• mprotect(Linux), VirtualProtect(Win)
• ぶっちゃけ原理は簡単(作り込みは大変)
• だが10年以上やってるといろいろ経験する
Xbyakの実装
20 / 45
• jnl(jump if not less)がIntelコンパイラでエラー
• UNIX系コンパイラは各種特殊数学関数を持っている
• jnlは次数nの第1種Bessel関数のlong double版
• j1とかy0とかynなど、そんなグローバル関数が!と思うもの
がいろいろ
• https://www.gnu.org/software/libc/manual/html_node/Speci
al-Functions.html
• コンパイラの実装によっては関数はマクロでもよいらしい
• C++17ではまともな名前でcmathに登場
• j1 → std::cyl_bessel_j(1, x)
• yn(n, x) → std::neumann(n, x)
いろいろなトラブル
21 / 45
• and関数を作る
• もちろんCでコンパイルできる
• がC++ではエラー
gccで通るがg++で通らないコード
>g++ -c c++ t.c
t.cpp:1:9: error: expected unqualified-id before 'int'
int and(int x, int y)
^~~
t.cpp:1:9: error: expected ')' before 'int'
t.cpp:1:9: error: expected initializer before 'int'
>gcc -c t.c
int and(int x, int y) {
return x & y;
}
22 / 45
• C++ではand, or, xor, not, and_eq, bitorなどが予約語
• 関数名に使えない
• VCではiso646.hをincludeしないなら使える
• gcc/clangでは-fno-operator-namesオプションで無効化
• うっかり忘れると一見意味不明な大量のエラー
• Xbyakでは
• and_(), or_()などアンダースコアをつけた名前に変更
• 後方互換性のためand(), or()などもサポート
• -fno-operator-namesなしで使おうとすると
"use -fno-operator-names option"という#errorを表示してる
• どうやって?
代替表現(Alternative representations)
23 / 45
• プリプロセッサの仕様を利用する
• notが予約語(operator~の代替表現)の場合
• #ifの後ろは~+0 = -1 ≠ 0となりtrueなので#errorで止まる
• notが予約語でない場合
• プリプロセッサの中で定義されないマクロの値は0
• 0 +0 = 0で#ifが実行されない
トリック(by @digitalghost)
#if not +0
#error "use -fno-operator-names"
#endif
24 / 45
• N = 32700ぐらいでエラー
• WindowsでならNがもっと大きくても動く
• メモリが足りないわけではない
Linuxでたくさんnewできないという報告
struct Code : Xbyak::CodeGenerator {
Code(int x) {
mov(eax, x);
ret();
}
};
std::vector<std::unique_ptr<Code>> v(N);
for (int i = 0; i < N; i++) {
v[i] = std::make_unique<Code>(i);
}
25 / 45
• 1プロセスあたりのメモリマップの上限
• デフォルト65536
• Xbyakのposix_memalign + mprotectは2個消費する
• スレッドも1スレッドあたり2個消費する
• 上限に達するとmprotectはENOMEMを返す
• XBYAK_USE_MMAP_ALLOCATORを定義すると大丈夫
• posix_memalignの代わりにmmapを使うallocator
• 何故かこの上限に掛からなくなる
• https://www.kernel.org/doc/Documentation/sysctl/vm.t
xtにはmmap, mprotect, madviseの呼び出しに影響とある
• 70万個とかでも作れるようになる
/proc/sys/vm/max_map_count
26 / 45
• 最初は従来のアセンブラのラベルを模倣
• ローカルラベル
• MASMライクな@@, @b, @f
古典的なラベル
L("loop");
...
dec(ecx);
jnz("loop");
inLocalLabel(); // ピリオドで始まるラベルは
L(".lp");
jmp(".lp");
outLocalLabel(); // この区間内でだけ有効なローカルラベル
27 / 45
• JITならではの要件
• ラベルは文字列ではなく変数であってほしい
柔軟なラベル
Label generate_function(int type) {
Label entry = L();
// typeに応じて関数生成
return entry;
}
Label add = generate_function(TypeAdd);
Label sub = generate_function(TypeSub);
Label mul = generate_function(TypeMul);
call(add);
28 / 45
• ラベルを即値として扱う
ジャンプテーブルも作りたい
Label labelTbl, L0, L1, L2;
mov(rax, labelTbl); // アドレスを代入
jmp(ptr [rax + rcx * sizeof(void*)]);
jmp(ptr [rip + L0]);// L0への相対アドレッシングジャンプ
// ジャンプテーブル
L(labelTbl);
putL(L0); // ラベルのアドレスをメモリに配置
putL(L1);
L(L0);
mov(a, ret0);
ret();
L(L1);
...
29 / 45
• コード生成時にジャンプ先は未定
• その後エラー処理コードを生成してから
generate_function()のエラー先を決定したい
プレースフォルダ的な使い方
std::pair<Label, Label> generate_function() {
Label entry = L();
Label error;
..
jmp(error); // エラー処理に飛ぶ(が飛び先未定)
return {entry, error};
}
30 / 45
• 2個のラベルをリンクさせる
assignL(dstLabel, srcLabel);
{add, err1} = gen_func(...); // 飛び先未定のラベル
{sub, err2} = gen_func(...); // 飛び先未定のラベル
closeErr = L();
// closeのエラー処理
criticalErr = L();
// criticalエラーの処理
asignL(err1, closeErr); //add関数内のエラーはcloseErr
asignL(err2, criticalErr); //sub関数のエラーはcriticalErr
31 / 45
• 8文字からなる言語
• [ ; ポインタが示す値が0なら]にジャンプ
• ] ; 対応する[にジャンプ
• while (*cur) { ... }に相当
BrainfuckのJIT
stack<Label> labelB, labelF;
case '[':
labelB.push(L());
mov(eax, cur);
test(eax, eax);
Label F;
jz(F, T_NEAR);
labelF.push(F);
break;
case ']':
jmp(labelB.top()); labelB.pop();
L(labelF.top()); labelF.pop();
break;
B: // [
mov rax, [rcx]
test eax, eax
jz F
... // ネストする
jmp B // ]
F:
32 / 45
• ラベルがL()でアドレス確定されるごとに
• そのラベルを参照している全ての未定義一覧のアドレス解決
• ラベルがジャンプ命令で指定されるごとに
• 既にラベル先が確定しているものはアドレス確定
• その時点で行き先が未定義のもののものは未定義一覧に追加
• + 相対ジャンプで管理すべきものもある
• assignL()はリンクを変更
• ローカルラベルやスコープを抜けたラベルは管理外に
• 数が多いので保持し続けるとラベル解決の速度劣化に
• ラベルはコピーされるとスコープの外に出ることがある
• 参照カウンタで管理
ラベル管理の内部
33 / 45
• L()やjmp(label)が呼ばれるごとに
• ローカルラベルやスコープを抜けたラベルは管理外に
• 数が多いので保持し続けるとラベル解決の速度劣化の要因
• ラベルはコピーされるとスコープの外に出ることがある
• 参照カウンタで管理
ラベル管理の内部
未定義ラベルULの集合U
ULにリンクするjmpの場所を保持
定義済みラベルDLの集合D
ULにリンクするjmpのaddrを解決
L(UL)で
ラベル追加
jmp(label)のlabelが
Dに無い
Dにある→addr解決
jmp(UL);
...
jnc(UL);
...
putL(UL);
assignL(UL, DL);で
ラベル移動
34 / 45
• JITコードはプロファイラを使っても分からない
• VTuneなどはそれぞれJITコードを教えるAPIがある
• cf. http://herumi.in.coocan.jp/prog/profile.html#USEVT
プロファイラ
35 / 45
• perfの場合/tmp/perf-<pid>.mapに1行ずつ
を書いておくと集計時に自動的に利用してくれる
perf with JIT code
アドレス サイズ 名前
PerfMap::set(const void *p, size_t n, const char *name) {
fprintf(fp, "%llx %zx %s¥n", (long long)p, n, name);
}
PerfMap pm;
pm.set(c.getCode(), c.getSize(), "fff");
pm.set(c2.getCode(), c2.getSize(), "ggg");
36 / 45
• testはnasm, yasmなどのツールの出力と比較して確認
• 新命令はツールが間違ってることが多いので悩ましい
• nasmは何度もバグ報告してる
• 昔はyasmの方が信頼性が高かったが最近更新されてない
• Intelのマニュアルが間違ってることもある
• 印象に残っているバグをいくつか
• VM上で未定義命令エラー
• cpuidを見てCPUが新命令に対応している判別してコード生成
• host CPUはその命令に対応しているがgestのVMは非対応
しかしVMはhostのcpuidを返していた
• 生成された命令を実行してillegal instruction(ややこしい)
バグ
37 / 45
• t.asm
• yasm -f win32 -l t.lst t.asm
• EBFEではなくEB00が正解
• 生成オブジェクトは正しいEB00を出力
• 正しく実行はできるので結構悩んだ
• チケットには2011年に登録されていた
• https://tortall.lighthouseapp.com/projects/78676/tickets/233
-byte-code-in-listing-differs-from-emitted-bytes
• 実は最新版でも直ってない
yasmのlstとobjの不一致
jmp L
L:
1 %line 1+1 t.asm
2 00000000 EBFE jmp L
3 L:
38 / 45
• 別の命令(1to2)を挟むと逆アセンブル結果がバグる
• 最初は正しい出力なので混乱した
• https://sourceware.org/bugzilla/show_bug.cgi?id=23025
• 報告して10時間でpatchが作成された
objdumpの逆アセンブル出力
>objdump -M x86-64 -D -b binary -m i386 vcvtpd2dq.bin
67 c5 fb e6 40 20 vcvtpd2dqx 0x20(%eax),%xmm0; (X)
67 c5 ff e6 40 20 vcvtpd2dqy 0x20(%eax),%xmm0; (Y)
67 62 f1 ff 18 e6 40 04 vcvtpd2dq 0x20(%eax){1to2},%xmm0
67 c5 fb e6 40 20 vcvtpd2dq 0x20(%eax),%xmm0 ; (X')
67 c5 ff e6 40 20 vcvtpd2dq 0x20(%eax),%xmm0 ; (Y')
39 / 45
• vgatherdps(zmm0|k1, ptr [rax + zmm18]); が
vgatherdps(zmm0|k1, ptr [rax + zmm2]);になるバグ
• VSIBエンコーディング
• 従来のSIB ; [eax + ebx * scale + offset] ; レジスタ8種類
• 64bit対応 ; 16種類レジスタを表現するため1bit増える
• その1bitはREXプレフィックスの中に
• VSIB ; AVX2でSIMDレジスタを指定できるようになった
XbyakのVSIBエンコーディングバグ
SDM2.3.12 Vector SIB(VSIB) Memory Addressing 40 / 45
• VSIBやREXでは足りない
• ?mm16~?mm31までのレジスタはどうやって指定?
• EVEX.vvvvビット
• addpd(zmm31, zmm30, zmm20);などはちゃんと実装していた
• SDM2.6.1 Instruction Format and EVEX
• VSIBのときEVEX.V'をoffにするのだった
• 単なる見落とだがVSIB Memory Addressingのところの記述は
変わってないし……(言い訳)
AVX-512でレジスタは32個
EVEXV’ High-16 NDS/VIDX register specifier
P[19] Combine with EVEX.vvvv or when VSIB present.
41 / 45
• cpuid(eax=0x0b, ecx=0/1)でSMTやCOREの数を取得
• cpuid(eax=0x4, ecx=cache_level)で各階層の情報を取得
• VMのせいか時々おかしい値に
キャッシュサイズの取得はややこしい
42 / 45
• Intel Software Development Emulator
• https://software.intel.com/en-us/articles/intel-software-
development-emulator
• 当たり前だが一番信頼できる
• xed -mpx -64 -ir <rawobj>でdisassemblerとして利用可能
• vgatherdps(zmm0|k1, ptr[rax + zmm2]);
の出力は正しくdisasできるのに
vgatherdps(zmm0|k1, ptr[rax + zmm0]);
の出力はエラー
• 両方とも同じエンコードパスを通るのに何故?
Intel SDEの仕様に悩む
ERROR: GATHER_REGS Could not decode at offset: 0x0
PC: 0x0: [62F27D49920400]
43 / 45
• gather命令はindex == destinationのとき使えない
• Note that: If any pair of the index, mask, or destination
registers are the same, this instruction results a UD
fault.
• decode errではなく実行時エラーならよかったんだが
エンコードは正しいがUDのため弾かれた
44 / 45
• 自分で小さいVM作ってみると面白いかも
• 狙ったコードを生成できると嬉しい
• 命令数が多い(マニュアルが分厚い)と大変だ
まとめ
45 / 45
1 de 45

Recomendados

条件分岐とcmovとmaxps por
条件分岐とcmovとmaxps条件分岐とcmovとmaxps
条件分岐とcmovとmaxpsMITSUNARI Shigeo
5.7K visualizações24 slides
Intro to SVE 富岳のA64FXを触ってみた por
Intro to SVE 富岳のA64FXを触ってみたIntro to SVE 富岳のA64FXを触ってみた
Intro to SVE 富岳のA64FXを触ってみたMITSUNARI Shigeo
11.6K visualizações24 slides
AVX-512(フォーマット)詳解 por
AVX-512(フォーマット)詳解AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解MITSUNARI Shigeo
8.9K visualizações29 slides
プログラムを高速化する話 por
プログラムを高速化する話プログラムを高速化する話
プログラムを高速化する話京大 マイコンクラブ
242.3K visualizações120 slides
組み込み関数(intrinsic)によるSIMD入門 por
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門Norishige Fukushima
47.5K visualizações122 slides
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編 por
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編
CPU / GPU高速化セミナー!性能モデルの理論と実践:実践編Fixstars Corporation
1.7K visualizações62 slides

Mais conteúdo relacionado

Mais procurados

プログラムを高速化する話Ⅱ 〜GPGPU編〜 por
プログラムを高速化する話Ⅱ 〜GPGPU編〜プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜京大 マイコンクラブ
67.4K visualizações184 slides
明日使えないすごいビット演算 por
明日使えないすごいビット演算明日使えないすごいビット演算
明日使えないすごいビット演算京大 マイコンクラブ
63.4K visualizações60 slides
いまさら聞けない!CUDA高速化入門 por
いまさら聞けない!CUDA高速化入門いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門Fixstars Corporation
5.3K visualizações90 slides
マルチコアを用いた画像処理 por
マルチコアを用いた画像処理マルチコアを用いた画像処理
マルチコアを用いた画像処理Norishige Fukushima
51.6K visualizações142 slides
いまさら聞けないarmを使ったNEONの基礎と活用事例 por
いまさら聞けないarmを使ったNEONの基礎と活用事例いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例Fixstars Corporation
5.1K visualizações102 slides
画像処理の高性能計算 por
画像処理の高性能計算画像処理の高性能計算
画像処理の高性能計算Norishige Fukushima
2.4K visualizações45 slides

Mais procurados(20)

プログラムを高速化する話Ⅱ 〜GPGPU編〜 por 京大 マイコンクラブ
プログラムを高速化する話Ⅱ 〜GPGPU編〜プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜
京大 マイコンクラブ67.4K visualizações
いまさら聞けない!CUDA高速化入門 por Fixstars Corporation
いまさら聞けない!CUDA高速化入門いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門
Fixstars Corporation5.3K visualizações
マルチコアを用いた画像処理 por Norishige Fukushima
マルチコアを用いた画像処理マルチコアを用いた画像処理
マルチコアを用いた画像処理
Norishige Fukushima51.6K visualizações
いまさら聞けないarmを使ったNEONの基礎と活用事例 por Fixstars Corporation
いまさら聞けないarmを使ったNEONの基礎と活用事例いまさら聞けないarmを使ったNEONの基礎と活用事例
いまさら聞けないarmを使ったNEONの基礎と活用事例
Fixstars Corporation5.1K visualizações
画像処理の高性能計算 por Norishige Fukushima
画像処理の高性能計算画像処理の高性能計算
画像処理の高性能計算
Norishige Fukushima2.4K visualizações
LLVM最適化のこつ por MITSUNARI Shigeo
LLVM最適化のこつLLVM最適化のこつ
LLVM最適化のこつ
MITSUNARI Shigeo7.4K visualizações
計算機アーキテクチャを考慮した高能率画像処理プログラミング por Norishige Fukushima
計算機アーキテクチャを考慮した高能率画像処理プログラミング計算機アーキテクチャを考慮した高能率画像処理プログラミング
計算機アーキテクチャを考慮した高能率画像処理プログラミング
Norishige Fukushima8.8K visualizações
SSE4.2の文字列処理命令の紹介 por MITSUNARI Shigeo
SSE4.2の文字列処理命令の紹介SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介
MITSUNARI Shigeo8.8K visualizações
20分くらいでわかった気分になれるC++20コルーチン por yohhoy
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン
yohhoy13K visualizações
冬のLock free祭り safe por Kumazaki Hiroki
冬のLock free祭り safe冬のLock free祭り safe
冬のLock free祭り safe
Kumazaki Hiroki27K visualizações
高速な倍精度指数関数expの実装 por MITSUNARI Shigeo
高速な倍精度指数関数expの実装高速な倍精度指数関数expの実装
高速な倍精度指数関数expの実装
MITSUNARI Shigeo15K visualizações
SIMDで整数除算 por shobomaru
SIMDで整数除算SIMDで整数除算
SIMDで整数除算
shobomaru7.8K visualizações
フラグを愛でる por MITSUNARI Shigeo
フラグを愛でるフラグを愛でる
フラグを愛でる
MITSUNARI Shigeo7.8K visualizações
CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編 por Fixstars Corporation
CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編
CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編
Fixstars Corporation1.6K visualizações
CUDAプログラミング入門 por NVIDIA Japan
CUDAプログラミング入門CUDAプログラミング入門
CUDAプログラミング入門
NVIDIA Japan29.1K visualizações
きつねさんでもわかるLlvm読書会 第2回 por Tomoya Kawanishi
きつねさんでもわかるLlvm読書会 第2回きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回
Tomoya Kawanishi9.7K visualizações
Slurmのジョブスケジューリングと実装 por Ryuichi Sakamoto
Slurmのジョブスケジューリングと実装Slurmのジョブスケジューリングと実装
Slurmのジョブスケジューリングと実装
Ryuichi Sakamoto1.9K visualizações
暗号技術の実装と数学 por MITSUNARI Shigeo
暗号技術の実装と数学暗号技術の実装と数学
暗号技術の実装と数学
MITSUNARI Shigeo9.6K visualizações
llvm入門 por MITSUNARI Shigeo
llvm入門llvm入門
llvm入門
MITSUNARI Shigeo16.6K visualizações

Similar a Xbyakの紹介とその周辺

高速な暗号実装のためにしてきたこと por
高速な暗号実装のためにしてきたこと高速な暗号実装のためにしてきたこと
高速な暗号実装のためにしてきたことMITSUNARI Shigeo
8.2K visualizações40 slides
Node予備校 vol.1 名古屋 por
Node予備校 vol.1 名古屋Node予備校 vol.1 名古屋
Node予備校 vol.1 名古屋Mori Shingo
2.9K visualizações46 slides
WASM(WebAssembly)入門 ペアリング演算やってみた por
WASM(WebAssembly)入門 ペアリング演算やってみたWASM(WebAssembly)入門 ペアリング演算やってみた
WASM(WebAssembly)入門 ペアリング演算やってみたMITSUNARI Shigeo
3.3K visualizações16 slides
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja) por
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)inaz2
6.8K visualizações38 slides
スタート低レイヤー #0 por
スタート低レイヤー #0スタート低レイヤー #0
スタート低レイヤー #0Kiwamu Okabe
3.1K visualizações27 slides
関東GPGPU勉強会 LLVM meets GPU por
関東GPGPU勉強会 LLVM meets GPU関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPUTakuro Iizuka
4.8K visualizações52 slides

Similar a Xbyakの紹介とその周辺(20)

高速な暗号実装のためにしてきたこと por MITSUNARI Shigeo
高速な暗号実装のためにしてきたこと高速な暗号実装のためにしてきたこと
高速な暗号実装のためにしてきたこと
MITSUNARI Shigeo8.2K visualizações
Node予備校 vol.1 名古屋 por Mori Shingo
Node予備校 vol.1 名古屋Node予備校 vol.1 名古屋
Node予備校 vol.1 名古屋
Mori Shingo2.9K visualizações
WASM(WebAssembly)入門 ペアリング演算やってみた por MITSUNARI Shigeo
WASM(WebAssembly)入門 ペアリング演算やってみたWASM(WebAssembly)入門 ペアリング演算やってみた
WASM(WebAssembly)入門 ペアリング演算やってみた
MITSUNARI Shigeo3.3K visualizações
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja) por inaz2
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
inaz26.8K visualizações
スタート低レイヤー #0 por Kiwamu Okabe
スタート低レイヤー #0スタート低レイヤー #0
スタート低レイヤー #0
Kiwamu Okabe3.1K visualizações
関東GPGPU勉強会 LLVM meets GPU por Takuro Iizuka
関東GPGPU勉強会 LLVM meets GPU関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU
Takuro Iizuka4.8K visualizações
SmartNews TechNight Vol5 : SmartNews AdServer 解体新書 / ポストモーテム por SmartNews, Inc.
SmartNews TechNight Vol5 : SmartNews AdServer 解体新書 / ポストモーテムSmartNews TechNight Vol5 : SmartNews AdServer 解体新書 / ポストモーテム
SmartNews TechNight Vol5 : SmartNews AdServer 解体新書 / ポストモーテム
SmartNews, Inc.32K visualizações
Pythonによる並列プログラミング -GPGPUも- por Yusaku Watanabe
Pythonによる並列プログラミング   -GPGPUも- Pythonによる並列プログラミング   -GPGPUも-
Pythonによる並列プログラミング -GPGPUも-
Yusaku Watanabe5K visualizações
StackExchangeで見たシステムプログラミング案件 por yaegashi
StackExchangeで見たシステムプログラミング案件StackExchangeで見たシステムプログラミング案件
StackExchangeで見たシステムプログラミング案件
yaegashi434 visualizações
明日使える超高速Ruby - RXbyak (Mitaka.rb #5) por Shuyo Nakatani
明日使える超高速Ruby - RXbyak (Mitaka.rb #5)明日使える超高速Ruby - RXbyak (Mitaka.rb #5)
明日使える超高速Ruby - RXbyak (Mitaka.rb #5)
Shuyo Nakatani2.4K visualizações
これからのコンピューティングとJava(Hacker Tackle) por なおき きしだ
これからのコンピューティングとJava(Hacker Tackle)これからのコンピューティングとJava(Hacker Tackle)
これからのコンピューティングとJava(Hacker Tackle)
なおき きしだ4.7K visualizações
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか) por Takeshi Yamamuro
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
Takeshi Yamamuro10.7K visualizações
ホームディレクトリに埋もれた便利なコードをさがせ! por Yohei Fushii
ホームディレクトリに埋もれた便利なコードをさがせ!ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!
Yohei Fushii27.6K visualizações
Fpga online seminar by fixstars (1st) por Fixstars Corporation
Fpga online seminar by fixstars (1st)Fpga online seminar by fixstars (1st)
Fpga online seminar by fixstars (1st)
Fixstars Corporation429 visualizações
ACRiウェビナー:岩渕様ご講演資料 por 直久 住川
ACRiウェビナー:岩渕様ご講演資料ACRiウェビナー:岩渕様ご講演資料
ACRiウェビナー:岩渕様ご講演資料
直久 住川364 visualizações
zend_parse_parametersと64bit環境 por Yo Ya
zend_parse_parametersと64bit環境zend_parse_parametersと64bit環境
zend_parse_parametersと64bit環境
Yo Ya5.2K visualizações
もしも… Javaでヘテロジニアスコアが使えたら… por Yasumasa Suenaga
もしも… Javaでヘテロジニアスコアが使えたら…もしも… Javaでヘテロジニアスコアが使えたら…
もしも… Javaでヘテロジニアスコアが使えたら…
Yasumasa Suenaga977 visualizações
Cプログラマのためのカッコつけないプログラミングの勧め por MITSUNARI Shigeo
Cプログラマのためのカッコつけないプログラミングの勧めCプログラマのためのカッコつけないプログラミングの勧め
Cプログラマのためのカッコつけないプログラミングの勧め
MITSUNARI Shigeo5.8K visualizações

Mais de MITSUNARI Shigeo

範囲証明つき準同型暗号とその対話的プロトコル por
範囲証明つき準同型暗号とその対話的プロトコル範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコルMITSUNARI Shigeo
2K visualizações23 slides
暗認本読書会13 advanced por
暗認本読書会13 advanced暗認本読書会13 advanced
暗認本読書会13 advancedMITSUNARI Shigeo
700 visualizações19 slides
暗認本読書会12 por
暗認本読書会12暗認本読書会12
暗認本読書会12MITSUNARI Shigeo
702 visualizações25 slides
暗認本読書会11 por
暗認本読書会11暗認本読書会11
暗認本読書会11MITSUNARI Shigeo
454 visualizações18 slides
暗認本読書会10 por
暗認本読書会10暗認本読書会10
暗認本読書会10MITSUNARI Shigeo
419 visualizações22 slides
暗認本読書会9 por
暗認本読書会9暗認本読書会9
暗認本読書会9MITSUNARI Shigeo
2.3K visualizações29 slides

Mais de MITSUNARI Shigeo(20)

範囲証明つき準同型暗号とその対話的プロトコル por MITSUNARI Shigeo
範囲証明つき準同型暗号とその対話的プロトコル範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコル
MITSUNARI Shigeo2K visualizações
暗認本読書会13 advanced por MITSUNARI Shigeo
暗認本読書会13 advanced暗認本読書会13 advanced
暗認本読書会13 advanced
MITSUNARI Shigeo700 visualizações
暗認本読書会12 por MITSUNARI Shigeo
暗認本読書会12暗認本読書会12
暗認本読書会12
MITSUNARI Shigeo702 visualizações
暗認本読書会11 por MITSUNARI Shigeo
暗認本読書会11暗認本読書会11
暗認本読書会11
MITSUNARI Shigeo454 visualizações
暗認本読書会10 por MITSUNARI Shigeo
暗認本読書会10暗認本読書会10
暗認本読書会10
MITSUNARI Shigeo419 visualizações
暗認本読書会9 por MITSUNARI Shigeo
暗認本読書会9暗認本読書会9
暗認本読書会9
MITSUNARI Shigeo2.3K visualizações
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen por MITSUNARI Shigeo
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
MITSUNARI Shigeo2.4K visualizações
暗認本読書会8 por MITSUNARI Shigeo
暗認本読書会8暗認本読書会8
暗認本読書会8
MITSUNARI Shigeo380 visualizações
暗認本読書会7 por MITSUNARI Shigeo
暗認本読書会7暗認本読書会7
暗認本読書会7
MITSUNARI Shigeo2.5K visualizações
暗認本読書会6 por MITSUNARI Shigeo
暗認本読書会6暗認本読書会6
暗認本読書会6
MITSUNARI Shigeo481 visualizações
暗認本読書会5 por MITSUNARI Shigeo
暗認本読書会5暗認本読書会5
暗認本読書会5
MITSUNARI Shigeo605 visualizações
暗認本読書会4 por MITSUNARI Shigeo
暗認本読書会4暗認本読書会4
暗認本読書会4
MITSUNARI Shigeo736 visualizações
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法 por MITSUNARI Shigeo
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
MITSUNARI Shigeo3.9K visualizações
私とOSSの25年 por MITSUNARI Shigeo
私とOSSの25年私とOSSの25年
私とOSSの25年
MITSUNARI Shigeo11.9K visualizações
WebAssembly向け多倍長演算の実装 por MITSUNARI Shigeo
WebAssembly向け多倍長演算の実装WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装
MITSUNARI Shigeo3.9K visualizações
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化 por MITSUNARI Shigeo
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
MITSUNARI Shigeo4K visualizações
楕円曲線と暗号 por MITSUNARI Shigeo
楕円曲線と暗号楕円曲線と暗号
楕円曲線と暗号
MITSUNARI Shigeo5.2K visualizações
HPC Phys-20201203 por MITSUNARI Shigeo
HPC Phys-20201203HPC Phys-20201203
HPC Phys-20201203
MITSUNARI Shigeo2.8K visualizações
BLS署名の実装とその応用 por MITSUNARI Shigeo
BLS署名の実装とその応用BLS署名の実装とその応用
BLS署名の実装とその応用
MITSUNARI Shigeo1.9K visualizações
LazyFP vulnerabilityの紹介 por MITSUNARI Shigeo
LazyFP vulnerabilityの紹介LazyFP vulnerabilityの紹介
LazyFP vulnerabilityの紹介
MITSUNARI Shigeo751 visualizações

Xbyakの紹介とその周辺

  • 2. • @herumi • https://github.com/herumi/she-wasm • ペアリングベースの準同型暗号のWebAssembly向け実装 • IEEE trans. on Computers 2014, AsiaCCS 2018など • https://github.com/herumi/bls • BLS署名の実装 • Dfinityというブロックチェーン系ベンチャーが使ってる 自己紹介 2 / 45
  • 3. • C++用のx86/x64専用JITアセンブラ • https://github.com/herumi/xbyak • 自分が使いたい(暗号用に開発を始めた)アセンブラ • 開発を初めてもうすぐ12年目 • AVX-512フルサポート • ちなみに現在(2018/9)Intelの一番長い名前の命令は • vgf2p8affineinvqb(17文字) • Galois Field Affine Transformation Inverse • 𝐹28の元𝑥に対して𝐴𝑥−1 + 𝐵を計算する Xbyakって何? 3 / 45
  • 4. • NASM, gasなどの通常の静的なアセンブラに比べて • コードを書きやすい(個人の感想) • C++との連係がしやすい(個人の...) • VMを書きやすい(略) • V8やWebkitなどのJavaScriptエンジンも同等の JITアセンブラを持ってる(ARMなどにも対応してる) 従来のアセンブラとの比較 4 / 45
  • 5. • 整数nを返す関数を生成するクラス • インスタンスを作成し関数ポインタを取り出して実行 実行時に整数nを返す関数を生成 struct Code : Xbyak::CodeGenerator { Code(int n) { mov(eax, n); // 注意 インラインアセンブラではない ret(); // 純粋なC++のコード } }; Code c1(3); auto f = c1.getCode<int (*)()>();// intを返す関数ポインタ printf("%d¥n", f()); // 3 実行時にコード生成 mov eax, 3 ret 5 / 45
  • 6. • MASMに似せるための各種演算子オーバーロード • Intel命令をそのまま書けるので直感的に操作しやすい • 小さなブロックを組み合わせて作る感覚が楽しい 雰囲気 void gen_add(const RegExp& dst, const RegExp& src); void f(int n) { auto addr = n > 0 ? rsi + rax * 8 + n * 8 : rdi; mov(rax, ptr[addr]); vgatherqpd(zmm5 | k7, ptr [rax + 64 + zmm21 * 2]); gen_add(rsp + 8, rsi + 8); } 6 / 45
  • 7. • template引数で長さを指定する整数クラス • 最大の大きさを指定するNはコンパイル時指定 • 実際の整数の大きさを指定するnは実行時指定 • このようなNを外部アセンブラと連係するのは面倒 固定多倍長加算 template<size_t N> struct Int { void init(size_t n); // n * 64 bit整数として利用(n < N) uint64_t d[N]; // z = x + y void (*add)(Int& z, const Int& x, const Int& y); }; 7 / 45
  • 8. • 64 * n-bit加算(ビット長に応じたコード生成) 多倍長加算の例 GenAdd(int n) { for (int i = 0; i < n; i++) { mov(rax, ptr [x+i*8]); if (i == 0) add(rax, ptr [y+i*8]); else adc(rax, ptr [y+i*8]); mov(ptr [z+i*8], rax); } ret(); } add3: mov rax, [rsi] add rax, [rdx] mov [rdi], rax mov rax, [rsi + 8] adc rax, [rdx + 8] mov [rdi + 8], rax mov rax, [rsi + 16] adc rax, [rdx + 16] mov [rdi + 16], rax ret add2: mov rax, [rsi] add rax, [rdx] mov [rdi], rax mov rax, [rsi + 8] adc rax, [rdx + 8] mov[rdi + 8], eax ret N=2 N=3 8 / 45
  • 9. • LLVMのbitコード(VMのコード)で書けば各種 CPUへの最適化コードが出力されるバラ色の世界 • と思っていたときもあった • 意外と各種アーキテクチャに縛られる • x64向けにはコード生成されるがARMではランタイムエラー • WebAssemblyへもランタイムエラー • 32bit/64bit CPU向けに別々にコードを書く必要(私の経験) • VMとは • 手書き最適化に比べて1~2割遅い(ことが多い) • Intel専用命令使いたい・勝手に使われたくない • もちろん開発コスト比を考慮すると○ LLVMのJITでええんじゃね? 9 / 45
  • 10. • LLVMの例 • x64での128bit演算出力 • 命令順序を除いて先ほどのコードと同一コードを生成 • 1024bitなら? 128~1024bitの加算 define void @add128(i128* %pz, i128* %px, i128* %py) { %x = load i128, i128* %px %y = load i128, i128* %py %z = add i128 %x, %y store i128 %z, i128* %pz ret void } 10 / 45
  • 11. • 怒濤のレジスタスピル(溢れ) • xとyを足してzに配置するとき 下位レジスタから順にやれば データの退避は不要 • それを把握していないので 一度スタックにコピーしてる • 無駄にSIMDを使って遅くなる こともある • 余談 • llcに-pre-RA-sched=list-ilp -max-sched-reorder=16 でspillしなくなる(たまたま?) • llc --help-hiddenすると大量の隠しオプションが現れる • バージョン非互換なものも多い 1024bitの加算の出力 movq 16(%rsi), %r13 movq (%rsi), %rbx movq 8(%rsi), %r15 movq 120(%rdx), %rax movq %rax, -8(%rsp) # 8-byte Spill movq 112(%rdx), %rax movq 104(%rdx), %rcx movq %rcx, -24(%rsp) # 8-byte Spill movq 96(%rdx), %rcx movq 88(%rdx), %rbp movq %rbp, -32(%rsp) # 8-byte Spill movq 80(%rdx), %r8 movq 72(%rdx), %r12 movq 64(%rdx), %r14 movq 56(%rdx), %rbp addq (%rdx), %rbx movq %rbx, -16(%rsp) # 8-byte Spill ... 11 / 45
  • 12. • PS2エミュレータ PCSX2 • https://github.com/PCSX2/pcsx2 ; plugin/shaderまわり • (当時)画像処理部分を画面のRGBのデータ並び(ビデオカ ードごとに違ってたりした)に応じた最適なコードをtempate を使ってコンパイル時コード生成 • バイナリコードの肥大化 • 実行時生成にすることでバイナリコードの削減と高速化 • mrubyのJIT by @miura1729さん • https://github.com/miura1729/mruby/ • 解説記事 https://qiita.com/miura1729/items/a1828849ec8fec596e74 • マンデルブロートなどの計算主体なものはx10ぐらいらしい 利用例 12 / 45
  • 13. • 正規表現JITエンジン by @sinya8282 • https://github.com/sinya8282/Regen • JavaScript VMのJIT by @Constellation • https://github.com/Constellation/iv/ • http://labs.cybozu.co.jp/youth.html サイボウズ・ラボユース生によるJIT 13 / 45
  • 14. • Citra(shaderのJIT部分に使ってるもよう) • https://github.com/citra-emu/citra • https://github.com/citra- emu/citra/blob/master/src/video_core/shader/shader_jit_x64_ compiler.cpp • log2やexp2なども実行時コード生成してる 3DSエミュレータ SSE4.1が使えれば それを利用 無ければ代替命令を 生成 14 / 45
  • 15. • 機械学習・深層学習ライブラリ • https://github.com/intel/mkl-dnn • 最新CPUのAVX-512命令v4fmaddpsなども利用 • jit_avx512_common_conv_winograd_kernel_f32.cpp • templateとlambdaとアセンブラの混在感が味わい深い? Intel MKL-DNN 15 / 45
  • 18. • CPUのL2, L3キャッシュサイズを取得 • https://github.com/intel/mkl- dnn/blob/19588d1484911a3dc7933b32ce71d2f1b9bbbb78/sr c/cpu/jit_avx512_core_fp32_wino_conv_2x3.cpp#L580 • データサイズやレジスタの個数を計算しながら適切な ループサイズを計算してJITしてる模様(詳細は未読) キャッシュサイズを意識 const int L2_cap = get_cache_size(2, true) / sizeof(float); const int L3_capacity = get_cache_size(3, false) / sizeof(float); 18 / 45
  • 19. • Intelによる小さい行列計算専用JITライブラリ • https://github.com/hfp/libxsmm • Cでゼロから作られてる(without Xbyak) • https://www.ixpug.org/images/docs/IXPUG_Annual_Spring_Co nference_2018/09-PABST-Libxsmm.pdf LIBXSMM 19 / 45
  • 20. • メモリを確保 • malloc/_aligned_malloc/posix_memalign • 命令フォーマットにしたがってバイトコードを展開 • modRM, SIB, rex, vex, evex, etc., AVX-512は結構大変 • exec属性を付与して実行 • mprotect(Linux), VirtualProtect(Win) • ぶっちゃけ原理は簡単(作り込みは大変) • だが10年以上やってるといろいろ経験する Xbyakの実装 20 / 45
  • 21. • jnl(jump if not less)がIntelコンパイラでエラー • UNIX系コンパイラは各種特殊数学関数を持っている • jnlは次数nの第1種Bessel関数のlong double版 • j1とかy0とかynなど、そんなグローバル関数が!と思うもの がいろいろ • https://www.gnu.org/software/libc/manual/html_node/Speci al-Functions.html • コンパイラの実装によっては関数はマクロでもよいらしい • C++17ではまともな名前でcmathに登場 • j1 → std::cyl_bessel_j(1, x) • yn(n, x) → std::neumann(n, x) いろいろなトラブル 21 / 45
  • 22. • and関数を作る • もちろんCでコンパイルできる • がC++ではエラー gccで通るがg++で通らないコード >g++ -c c++ t.c t.cpp:1:9: error: expected unqualified-id before 'int' int and(int x, int y) ^~~ t.cpp:1:9: error: expected ')' before 'int' t.cpp:1:9: error: expected initializer before 'int' >gcc -c t.c int and(int x, int y) { return x & y; } 22 / 45
  • 23. • C++ではand, or, xor, not, and_eq, bitorなどが予約語 • 関数名に使えない • VCではiso646.hをincludeしないなら使える • gcc/clangでは-fno-operator-namesオプションで無効化 • うっかり忘れると一見意味不明な大量のエラー • Xbyakでは • and_(), or_()などアンダースコアをつけた名前に変更 • 後方互換性のためand(), or()などもサポート • -fno-operator-namesなしで使おうとすると "use -fno-operator-names option"という#errorを表示してる • どうやって? 代替表現(Alternative representations) 23 / 45
  • 24. • プリプロセッサの仕様を利用する • notが予約語(operator~の代替表現)の場合 • #ifの後ろは~+0 = -1 ≠ 0となりtrueなので#errorで止まる • notが予約語でない場合 • プリプロセッサの中で定義されないマクロの値は0 • 0 +0 = 0で#ifが実行されない トリック(by @digitalghost) #if not +0 #error "use -fno-operator-names" #endif 24 / 45
  • 25. • N = 32700ぐらいでエラー • WindowsでならNがもっと大きくても動く • メモリが足りないわけではない Linuxでたくさんnewできないという報告 struct Code : Xbyak::CodeGenerator { Code(int x) { mov(eax, x); ret(); } }; std::vector<std::unique_ptr<Code>> v(N); for (int i = 0; i < N; i++) { v[i] = std::make_unique<Code>(i); } 25 / 45
  • 26. • 1プロセスあたりのメモリマップの上限 • デフォルト65536 • Xbyakのposix_memalign + mprotectは2個消費する • スレッドも1スレッドあたり2個消費する • 上限に達するとmprotectはENOMEMを返す • XBYAK_USE_MMAP_ALLOCATORを定義すると大丈夫 • posix_memalignの代わりにmmapを使うallocator • 何故かこの上限に掛からなくなる • https://www.kernel.org/doc/Documentation/sysctl/vm.t xtにはmmap, mprotect, madviseの呼び出しに影響とある • 70万個とかでも作れるようになる /proc/sys/vm/max_map_count 26 / 45
  • 27. • 最初は従来のアセンブラのラベルを模倣 • ローカルラベル • MASMライクな@@, @b, @f 古典的なラベル L("loop"); ... dec(ecx); jnz("loop"); inLocalLabel(); // ピリオドで始まるラベルは L(".lp"); jmp(".lp"); outLocalLabel(); // この区間内でだけ有効なローカルラベル 27 / 45
  • 28. • JITならではの要件 • ラベルは文字列ではなく変数であってほしい 柔軟なラベル Label generate_function(int type) { Label entry = L(); // typeに応じて関数生成 return entry; } Label add = generate_function(TypeAdd); Label sub = generate_function(TypeSub); Label mul = generate_function(TypeMul); call(add); 28 / 45
  • 29. • ラベルを即値として扱う ジャンプテーブルも作りたい Label labelTbl, L0, L1, L2; mov(rax, labelTbl); // アドレスを代入 jmp(ptr [rax + rcx * sizeof(void*)]); jmp(ptr [rip + L0]);// L0への相対アドレッシングジャンプ // ジャンプテーブル L(labelTbl); putL(L0); // ラベルのアドレスをメモリに配置 putL(L1); L(L0); mov(a, ret0); ret(); L(L1); ... 29 / 45
  • 30. • コード生成時にジャンプ先は未定 • その後エラー処理コードを生成してから generate_function()のエラー先を決定したい プレースフォルダ的な使い方 std::pair<Label, Label> generate_function() { Label entry = L(); Label error; .. jmp(error); // エラー処理に飛ぶ(が飛び先未定) return {entry, error}; } 30 / 45
  • 31. • 2個のラベルをリンクさせる assignL(dstLabel, srcLabel); {add, err1} = gen_func(...); // 飛び先未定のラベル {sub, err2} = gen_func(...); // 飛び先未定のラベル closeErr = L(); // closeのエラー処理 criticalErr = L(); // criticalエラーの処理 asignL(err1, closeErr); //add関数内のエラーはcloseErr asignL(err2, criticalErr); //sub関数のエラーはcriticalErr 31 / 45
  • 32. • 8文字からなる言語 • [ ; ポインタが示す値が0なら]にジャンプ • ] ; 対応する[にジャンプ • while (*cur) { ... }に相当 BrainfuckのJIT stack<Label> labelB, labelF; case '[': labelB.push(L()); mov(eax, cur); test(eax, eax); Label F; jz(F, T_NEAR); labelF.push(F); break; case ']': jmp(labelB.top()); labelB.pop(); L(labelF.top()); labelF.pop(); break; B: // [ mov rax, [rcx] test eax, eax jz F ... // ネストする jmp B // ] F: 32 / 45
  • 33. • ラベルがL()でアドレス確定されるごとに • そのラベルを参照している全ての未定義一覧のアドレス解決 • ラベルがジャンプ命令で指定されるごとに • 既にラベル先が確定しているものはアドレス確定 • その時点で行き先が未定義のもののものは未定義一覧に追加 • + 相対ジャンプで管理すべきものもある • assignL()はリンクを変更 • ローカルラベルやスコープを抜けたラベルは管理外に • 数が多いので保持し続けるとラベル解決の速度劣化に • ラベルはコピーされるとスコープの外に出ることがある • 参照カウンタで管理 ラベル管理の内部 33 / 45
  • 34. • L()やjmp(label)が呼ばれるごとに • ローカルラベルやスコープを抜けたラベルは管理外に • 数が多いので保持し続けるとラベル解決の速度劣化の要因 • ラベルはコピーされるとスコープの外に出ることがある • 参照カウンタで管理 ラベル管理の内部 未定義ラベルULの集合U ULにリンクするjmpの場所を保持 定義済みラベルDLの集合D ULにリンクするjmpのaddrを解決 L(UL)で ラベル追加 jmp(label)のlabelが Dに無い Dにある→addr解決 jmp(UL); ... jnc(UL); ... putL(UL); assignL(UL, DL);で ラベル移動 34 / 45
  • 36. • perfの場合/tmp/perf-<pid>.mapに1行ずつ を書いておくと集計時に自動的に利用してくれる perf with JIT code アドレス サイズ 名前 PerfMap::set(const void *p, size_t n, const char *name) { fprintf(fp, "%llx %zx %s¥n", (long long)p, n, name); } PerfMap pm; pm.set(c.getCode(), c.getSize(), "fff"); pm.set(c2.getCode(), c2.getSize(), "ggg"); 36 / 45
  • 37. • testはnasm, yasmなどのツールの出力と比較して確認 • 新命令はツールが間違ってることが多いので悩ましい • nasmは何度もバグ報告してる • 昔はyasmの方が信頼性が高かったが最近更新されてない • Intelのマニュアルが間違ってることもある • 印象に残っているバグをいくつか • VM上で未定義命令エラー • cpuidを見てCPUが新命令に対応している判別してコード生成 • host CPUはその命令に対応しているがgestのVMは非対応 しかしVMはhostのcpuidを返していた • 生成された命令を実行してillegal instruction(ややこしい) バグ 37 / 45
  • 38. • t.asm • yasm -f win32 -l t.lst t.asm • EBFEではなくEB00が正解 • 生成オブジェクトは正しいEB00を出力 • 正しく実行はできるので結構悩んだ • チケットには2011年に登録されていた • https://tortall.lighthouseapp.com/projects/78676/tickets/233 -byte-code-in-listing-differs-from-emitted-bytes • 実は最新版でも直ってない yasmのlstとobjの不一致 jmp L L: 1 %line 1+1 t.asm 2 00000000 EBFE jmp L 3 L: 38 / 45
  • 39. • 別の命令(1to2)を挟むと逆アセンブル結果がバグる • 最初は正しい出力なので混乱した • https://sourceware.org/bugzilla/show_bug.cgi?id=23025 • 報告して10時間でpatchが作成された objdumpの逆アセンブル出力 >objdump -M x86-64 -D -b binary -m i386 vcvtpd2dq.bin 67 c5 fb e6 40 20 vcvtpd2dqx 0x20(%eax),%xmm0; (X) 67 c5 ff e6 40 20 vcvtpd2dqy 0x20(%eax),%xmm0; (Y) 67 62 f1 ff 18 e6 40 04 vcvtpd2dq 0x20(%eax){1to2},%xmm0 67 c5 fb e6 40 20 vcvtpd2dq 0x20(%eax),%xmm0 ; (X') 67 c5 ff e6 40 20 vcvtpd2dq 0x20(%eax),%xmm0 ; (Y') 39 / 45
  • 40. • vgatherdps(zmm0|k1, ptr [rax + zmm18]); が vgatherdps(zmm0|k1, ptr [rax + zmm2]);になるバグ • VSIBエンコーディング • 従来のSIB ; [eax + ebx * scale + offset] ; レジスタ8種類 • 64bit対応 ; 16種類レジスタを表現するため1bit増える • その1bitはREXプレフィックスの中に • VSIB ; AVX2でSIMDレジスタを指定できるようになった XbyakのVSIBエンコーディングバグ SDM2.3.12 Vector SIB(VSIB) Memory Addressing 40 / 45
  • 41. • VSIBやREXでは足りない • ?mm16~?mm31までのレジスタはどうやって指定? • EVEX.vvvvビット • addpd(zmm31, zmm30, zmm20);などはちゃんと実装していた • SDM2.6.1 Instruction Format and EVEX • VSIBのときEVEX.V'をoffにするのだった • 単なる見落とだがVSIB Memory Addressingのところの記述は 変わってないし……(言い訳) AVX-512でレジスタは32個 EVEXV’ High-16 NDS/VIDX register specifier P[19] Combine with EVEX.vvvv or when VSIB present. 41 / 45
  • 42. • cpuid(eax=0x0b, ecx=0/1)でSMTやCOREの数を取得 • cpuid(eax=0x4, ecx=cache_level)で各階層の情報を取得 • VMのせいか時々おかしい値に キャッシュサイズの取得はややこしい 42 / 45
  • 43. • Intel Software Development Emulator • https://software.intel.com/en-us/articles/intel-software- development-emulator • 当たり前だが一番信頼できる • xed -mpx -64 -ir <rawobj>でdisassemblerとして利用可能 • vgatherdps(zmm0|k1, ptr[rax + zmm2]); の出力は正しくdisasできるのに vgatherdps(zmm0|k1, ptr[rax + zmm0]); の出力はエラー • 両方とも同じエンコードパスを通るのに何故? Intel SDEの仕様に悩む ERROR: GATHER_REGS Could not decode at offset: 0x0 PC: 0x0: [62F27D49920400] 43 / 45
  • 44. • gather命令はindex == destinationのとき使えない • Note that: If any pair of the index, mask, or destination registers are the same, this instruction results a UD fault. • decode errではなく実行時エラーならよかったんだが エンコードは正しいがUDのため弾かれた 44 / 45
  • 45. • 自分で小さいVM作ってみると面白いかも • 狙ったコードを生成できると嬉しい • 命令数が多い(マニュアルが分厚い)と大変だ まとめ 45 / 45