Upgrade to Pro — share decks privately, control downloads, hide ads and more …

LinuxとMN-Coreコンパイラランタイムにおける_プログラムの起動プロセスとその比較

Preferred Networks
December 13, 2024
5

 LinuxとMN-Coreコンパイラランタイムにおける_プログラムの起動プロセスとその比較

Preferred Networks

December 13, 2024
Tweet

More Decks by Preferred Networks

Transcript

  1. 2 • プログラムの起動の流れという観点で LinuxとMN-Core コンパイラ・ランタイムは似ている • Linux のグローバル変数は MN-Core コンパイラ・ランタイ

    ムにおけるモデルのパラメータに対応する • どちらもグローバル変数を実現するために リロケーションという機構を使う 今日のおはなし
  2. 3 • Linux におけるプログラムの起動プロセス ◦ グローバル変数とは何か ◦ Linux におけるプログラムの起動の流れ ▪

    リロケーションとグローバル変数 • MN-Core コンパイラ・ランタイム ◦ MN-Core コンパイラ・ランタイムにおけるグローバル変数とは何か ◦ プログラムの起動の流れ ▪ リロケーションとグローバル変数 • まとめ 目次
  3. 4 • Linux におけるプログラムの起動プロセス ◦ グローバル変数とは何か ◦ Linux におけるプログラムの起動の流れ ▪

    リロケーションとグローバル変数 • MN-Core コンパイラ・ランタイム ◦ MN-Core コンパイラ・ランタイムにおけるグローバル変数とは何か ◦ プログラムの起動の流れ ▪ リロケーションとグローバル変数 • まとめ 目次
  4. 6 /* param.c */ #include <stdint.h> uint64_t param = 0xdeadbeefdeadbeef;

    グローバル変数を使うサンプルプログラム /* train_step.c */ #include <stdint.h> extern uint64_t param; void train_step(){ param++; } /* main.c */ void train_step(); void eval(); int main(){ train_step(); train_step(); train_step(); eval(); return 0; } /* eval.c */ #include <stdint.h> #include <stdio.h> extern uint64_t param; void eval(){ printf("param: %lx\n", param); }
  5. 7 /* param.c */ #include <stdint.h> uint64_t param = 0xdeadbeefdeadbeef;

    グローバル変数を使うサンプルプログラム /* train_step.c */ #include <stdint.h> extern uint64_t param; void train_step(){ param++; } /* main.c */ void train_step(); void eval(); int main(){ train_step(); train_step(); train_step(); eval(); return 0; } /* eval.c */ #include <stdint.h> #include <stdio.h> extern uint64_t param; void eval(){ printf("param: %lx\n", param); } グローバル変数 を定義 グローバル変数 を更新 グローバル変数の値 を表示
  6. 8 • 機械学習の学習と評価の流れを模擬したもの ◦ param.c: モデルの定義 ◦ train_step.c: 学習の1ステップ。モデルのパラメータを更新する ◦

    eval.c: 学習したパラメータを利用して計算する ◦ main.c: メイン関数 サンプルプログラムの解説
  7. 9 $ gcc param.c -o param.so -shared -fPIC -g $

    gcc train_step.c param.so -o train_step.so -shared -fPIC -g $ gcc eval.c param.so -o eval.so -shared -fPIC -g $ gcc main.c param.so train_step.so eval.so -o main -g $ LD_LIBRARY_PATH=. ./main param: deadbeefdeadbef2 $ ls eval.c eval.so* main* main.c param.c param.so* run.sh* train_step.c train_step.so* サンプルプログラムのコンパイルと実行 • main の他に eval.so、train_step.so、param.so という共有ライブラリがあり、 実行時にメモリに読み込まれる
  8. 11 train_step.so をディスアセンブルしてみる $ objdump --disassemble=train_step -M intel train_step.so --no-show-raw-insn

    00000000000010f9 <train_step>: 10f9: endbr64 10fd: push rbp 10fe: mov rbp,rsp 1101: mov rax,QWORD PTR [rip+0x2ed0] # 3fd8 <param> 1108: mov rax,QWORD PTR [rax] 110b: lea rdx,[rax+0x1] 110f: mov rax,QWORD PTR [rip+0x2ec2] # 3fd8 <param> 1116: mov QWORD PTR [rax],rdx 1119: nop 111a: pop rbp 111b: ret
  9. 12 train_step.so をディスアセンブルしてみる $ objdump --disassemble=train_step -M intel train_step.so --no-show-raw-insn

    00000000000010f9 <train_step>: 10f9: endbr64 10fd: push rbp 10fe: mov rbp,rsp 1101: mov rax,QWORD PTR [rip+0x2ed0] # 3fd8 <param> 1108: mov rax,QWORD PTR [rax] 110b: lea rdx,[rax+0x1] 110f: mov rax,QWORD PTR [rip+0x2ec2] # 3fd8 <param> 1116: mov QWORD PTR [rax],rdx 1119: nop 111a: pop rbp 111b: ret param++; rax に入っているのは &param rip + 0x2ed0(3fd8) に入っているのは &&param
  10. 13 $ readelf -l train_step.so ---------- 省略 ---------- LOAD 0x0000000000002e68

    0x0000000000003e68 0x0000000000003e68 0x00000000000001b8 0x00000000000001c0 RW 0x1000 ---------- 省略 ---------- 0x3fd8 には何が入っているのか • train_step.so の 0x2e68 から 0x3020 (= 0x2e68 + 0x01b8) が メモリ空間の 0x3e68 から 0x4028 (= 0x3e68 + 0x01c0) にマップされる ◦ 足りない部分は 0 埋めされる ◦ (本当はこのアドレスにランダムなオフセットが足される) • メモリ空間の 0x3fd8 の値を知りたければ train_step.so の 0x2fd8 を見ればよい
  11. 14 $ xxd train_step.so | grep 2fd0 -A 3 -B

    3 00002fa0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00002fb0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00002fc0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00002fd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00002fe0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00002ff0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00003000: 783e 0000 0000 0000 0000 0000 0000 0000 x>.............. train_step.so には 0 が埋まっていた • メモリ空間の 0x3fd8 に対応する train_step.so の 0x2fd8 には 0 が埋まっている • このままでは動かない ◦ NULL ポインタを参照して壊れる • 起動時に何かしているはず ...何をしているのか ?
  12. 15 • Linux におけるプログラムの起動プロセス ◦ グローバル変数とは何か ◦ Linux におけるプログラムの起動の流れ ▪

    リロケーションとグローバル変数 • MN-Core コンパイラ・ランタイム ◦ MN-Core コンパイラ・ランタイムにおけるグローバル変数とは何か ◦ プログラムの起動の流れ ▪ リロケーションとグローバル変数 • まとめ 目次
  13. 16 1. bash や zsh などのシェルが execve(2) を発行 ◦ Linux

    kernel に制御がわたる 2. Linux kernel が execve(2) を実行 ◦ Linux kernel とは OS のコア部分 ◦ ld-linux.so に制御がわたる 3. ld-linux.so が main を起動 ◦ ld-linux.so とはプログラムをメモリ上に展開するプログラム ◦ main と依存する共有ライブラリをメモリ上に展開 ◦ リロケーション ◦ main に制御がわたる Linux におけるプログラムの起動の流れ
  14. 17 $ strace ./main execve("./main", ["./main"], 0x7ffc0413c370 /* 174 vars

    */) = 0 ---------- 以下略 ---------- bash や zsh などのシェルが execve(2) を発行 • strace(1) を利用すると発行されたシステムコールを見ることができます
  15. 18 • SYSCALL_DEFINE3(execve ... @fs/exec.c#L2172-L2178 • do_execve @ fs/exec.c#L2095-L2102 •

    do_execveat_common @ fs/exec.c#L1951-L2034 • bprm_execve @ fs/exec.c#L1896-L1949 • exec_binprm @ fs/exec.c#L1851-L1894 • search_binary_handler @ fs/exec.c#L1805-L1849 • linux_binfmt @ include/linux/binfmts.h#L82-L91 • elf_format @ fs/binfmt_elf.c#L101-L109 • load_elf_binary @ fs/binfmt_elf.c#L819-L1350 ◦ interpreter != NULL のケースを追う • start_thread @ arch/x86/kernel/process_64.c#L581-L587 Linux kernel が execve(2) を実行
  16. 20 $ LD_LIBRARY_PATH=. gdb main -batch -ex "break main" -ex

    "run" -ex "info proc mappings" Breakpoint 1 at 0x1171: file main.c, line 6. warning: Error disabling address space randomization: Operation not permitted [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 1, main () at main.c:6 6 train_step(); process 1419819 Mapped address spaces: Start Addr End Addr Size Offset Perms objfile 0x5d94ab679000 0x5d94ab67a000 0x1000 0x0 r--p /tmp/main 0x5d94ab67a000 0x5d94ab67b000 0x1000 0x1000 r-xp /tmp/main 0x5d94ab67b000 0x5d94ab67c000 0x1000 0x2000 r--p /tmp/main 0x5d94ab67c000 0x5d94ab67d000 0x1000 0x2000 r--p /tmp/main 0x5d94ab67d000 0x5d94ab67e000 0x1000 0x3000 rw-p /tmp/main 0x7a9505e4a000 0x7a9505e4c000 0x2000 0x0 rw-p 0x7a9505e4c000 0x7a9505e4d000 0x1000 0x0 r--p /tmp/param.so 0x7a9505e4d000 0x7a9505e4e000 0x1000 0x1000 r-xp /tmp/param.so 0x7a9505e4e000 0x7a9505e4f000 0x1000 0x2000 r--p /tmp/param.so 0x7a9505e4f000 0x7a9505e50000 0x1000 0x2000 r--p /tmp/param.so 0x7a9505e50000 0x7a9505e51000 0x1000 0x3000 rw-p /tmp/param.so 0x7a9505e51000 0x7a9505e79000 0x28000 0x0 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a9505e79000 0x7a950600e000 0x195000 0x28000 r-xp /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a950600e000 0x7a9506066000 0x58000 0x1bd000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a9506066000 0x7a9506067000 0x1000 0x215000 ---p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a9506067000 0x7a950606b000 0x4000 0x215000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a950606b000 0x7a950606d000 0x2000 0x219000 rw-p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a950606d000 0x7a950607a000 0xd000 0x0 rw-p 0x7a9506097000 0x7a9506098000 0x1000 0x0 r--p /tmp/eval.so 0x7a9506098000 0x7a9506099000 0x1000 0x1000 r-xp /tmp/eval.so 0x7a9506099000 0x7a950609a000 0x1000 0x2000 r--p /tmp/eval.so 0x7a950609a000 0x7a950609b000 0x1000 0x2000 r--p /tmp/eval.so 0x7a950609b000 0x7a950609c000 0x1000 0x3000 rw-p /tmp/eval.so 0x7a950609c000 0x7a950609d000 0x1000 0x0 r--p /tmp/train_step.so 0x7a950609d000 0x7a950609e000 0x1000 0x1000 r-xp /tmp/train_step.so 0x7a950609e000 0x7a950609f000 0x1000 0x2000 r--p /tmp/train_step.so 0x7a950609f000 0x7a95060a0000 0x1000 0x2000 r--p /tmp/train_step.so 0x7a95060a0000 0x7a95060a1000 0x1000 0x3000 rw-p /tmp/train_step.so 0x7a95060a1000 0x7a95060a3000 0x2000 0x0 rw-p 0x7a95060a3000 0x7a95060a5000 0x2000 0x0 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7a95060a5000 0x7a95060cf000 0x2a000 0x2000 r-xp /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7a95060cf000 0x7a95060da000 0xb000 0x2c000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7a95060db000 0x7a95060dd000 0x2000 0x37000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7a95060dd000 0x7a95060df000 0x2000 0x39000 rw-p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7ffebaac2000 0x7ffebaae5000 0x23000 0x0 rw-p [stack] 0x7ffebabd5000 0x7ffebabd9000 0x4000 0x0 r--p [vvar] 0x7ffebabd9000 0x7ffebabdb000 0x2000 0x0 r-xp [vdso] 0xffffffffff600000 0xffffffffff601000 0x1000 0x0 --xp [vsyscall] main と依存する共有ライブラリをメモリ上に展開
  17. 21 $ LD_LIBRARY_PATH=. gdb main -batch -ex "break main" -ex

    "run" -ex "info proc mappings" Breakpoint 1 at 0x1171: file main.c, line 6. warning: Error disabling address space randomization: Operation not permitted [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 1, main () at main.c:6 6 train_step(); process 1419819 Mapped address spaces: Start Addr End Addr Size Offset Perms objfile 0x5d94ab679000 0x5d94ab67a000 0x1000 0x0 r--p /tmp/main 0x5d94ab67a000 0x5d94ab67b000 0x1000 0x1000 r-xp /tmp/main 0x5d94ab67b000 0x5d94ab67c000 0x1000 0x2000 r--p /tmp/main 0x5d94ab67c000 0x5d94ab67d000 0x1000 0x2000 r--p /tmp/main 0x5d94ab67d000 0x5d94ab67e000 0x1000 0x3000 rw-p /tmp/main 0x7a9505e4a000 0x7a9505e4c000 0x2000 0x0 rw-p 0x7a9505e4c000 0x7a9505e4d000 0x1000 0x0 r--p /tmp/param.so 0x7a9505e4d000 0x7a9505e4e000 0x1000 0x1000 r-xp /tmp/param.so 0x7a9505e4e000 0x7a9505e4f000 0x1000 0x2000 r--p /tmp/param.so 0x7a9505e4f000 0x7a9505e50000 0x1000 0x2000 r--p /tmp/param.so 0x7a9505e50000 0x7a9505e51000 0x1000 0x3000 rw-p /tmp/param.so 0x7a9505e51000 0x7a9505e79000 0x28000 0x0 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a9505e79000 0x7a950600e000 0x195000 0x28000 r-xp /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a950600e000 0x7a9506066000 0x58000 0x1bd000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a9506066000 0x7a9506067000 0x1000 0x215000 ---p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a9506067000 0x7a950606b000 0x4000 0x215000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a950606b000 0x7a950606d000 0x2000 0x219000 rw-p /usr/lib/x86_64-linux-gnu/libc.so.6 0x7a950606d000 0x7a950607a000 0xd000 0x0 rw-p 0x7a9506097000 0x7a9506098000 0x1000 0x0 r--p /tmp/eval.so 0x7a9506098000 0x7a9506099000 0x1000 0x1000 r-xp /tmp/eval.so 0x7a9506099000 0x7a950609a000 0x1000 0x2000 r--p /tmp/eval.so 0x7a950609a000 0x7a950609b000 0x1000 0x2000 r--p /tmp/eval.so 0x7a950609b000 0x7a950609c000 0x1000 0x3000 rw-p /tmp/eval.so 0x7a950609c000 0x7a950609d000 0x1000 0x0 r--p /tmp/train_step.so 0x7a950609d000 0x7a950609e000 0x1000 0x1000 r-xp /tmp/train_step.so 0x7a950609e000 0x7a950609f000 0x1000 0x2000 r--p /tmp/train_step.so 0x7a950609f000 0x7a95060a0000 0x1000 0x2000 r--p /tmp/train_step.so 0x7a95060a0000 0x7a95060a1000 0x1000 0x3000 rw-p /tmp/train_step.so 0x7a95060a1000 0x7a95060a3000 0x2000 0x0 rw-p 0x7a95060a3000 0x7a95060a5000 0x2000 0x0 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7a95060a5000 0x7a95060cf000 0x2a000 0x2000 r-xp /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7a95060cf000 0x7a95060da000 0xb000 0x2c000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7a95060db000 0x7a95060dd000 0x2000 0x37000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7a95060dd000 0x7a95060df000 0x2000 0x39000 rw-p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 0x7ffebaac2000 0x7ffebaae5000 0x23000 0x0 rw-p [stack] 0x7ffebabd5000 0x7ffebabd9000 0x4000 0x0 r--p [vvar] 0x7ffebabd9000 0x7ffebabdb000 0x2000 0x0 r-xp [vdso] 0xffffffffff600000 0xffffffffff601000 0x1000 0x0 --xp [vsyscall] main と依存する共有ライブラリをメモリ上に展開 main param.so eval.so train_step.so
  18. 22 • メモリ上に展開されたプログラムを実行直前に書き換える処理 • ld-linux.so はリロケーション情報を元に書き換えを行う ◦ elf_machine_rela @ sysdeps/x86_64/dl-machine.h#L356-L359

    リロケーション $ readelf -r train_step.so Relocation section '.rela.dyn' at offset 0x430 contains 8 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000003e68 000000000008 R_X86_64_RELATIVE 10f0 000000003e70 000000000008 R_X86_64_RELATIVE 10b0 000000004018 000000000008 R_X86_64_RELATIVE 4018 000000003fd8 000100000006 R_X86_64_GLOB_DAT 0000000000000000 param + 0 000000003fe0 000200000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize + 0 000000003fe8 000300000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCl[...] + 0 000000003ff0 000400000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTM[...] + 0 000000003ff8 000500000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
  19. 23 • メモリ上に展開されたプログラムを実行直前に書き換える処理 • ld-linux.so はリロケーション情報を元に書き換えを行う ◦ elf_machine_rela @ sysdeps/x86_64/dl-machine.h#L356-L359

    リロケーション $ readelf -r train_step.so Relocation section '.rela.dyn' at offset 0x430 contains 8 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000003e68 000000000008 R_X86_64_RELATIVE 10f0 000000003e70 000000000008 R_X86_64_RELATIVE 10b0 000000004018 000000000008 R_X86_64_RELATIVE 4018 000000003fd8 000100000006 R_X86_64_GLOB_DAT 0000000000000000 param + 0 000000003fe0 000200000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize + 0 000000003fe8 000300000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCl[...] + 0 000000003ff0 000400000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTM[...] + 0 000000003ff8 000500000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0 メモリ上の 0x3fd8 に param のアドレスを埋めろと書いてある
  20. 24 $ LD_LIBRARY_PATH=. gdb main -ex "break main" -ex "run"

    (gdb) disassemble train_step Dump of assembler code for function train_step: 0x00007687ba88b0f9 <+0>: endbr64 0x00007687ba88b0fd <+4>: push rbp 0x00007687ba88b0fe <+5>: mov rbp,rsp 0x00007687ba88b101 <+8>: mov rax,QWORD PTR [rip+0x2ed0] # 0x7687ba88dfd8 0x00007687ba88b108 <+15>: mov rax,QWORD PTR [rax] 0x00007687ba88b10b <+18>: lea rdx,[rax+0x1] 0x00007687ba88b10f <+22>: mov rax,QWORD PTR [rip+0x2ec2] # 0x7687ba88dfd8 0x00007687ba88b116 <+29>: mov QWORD PTR [rax],rdx 0x00007687ba88b119 <+32>: nop 0x00007687ba88b11a <+33>: pop rbp 0x00007687ba88b11b <+34>: ret End of assembler dump. (gdb) print/x **(unsigned long long **)0x7687ba88dfd8 $1 = 0xdeadbeefdeadbeef リロケーションとグローバル変数
  21. 25 $ LD_LIBRARY_PATH=. gdb main -ex "break main" -ex "run"

    (gdb) disassemble train_step Dump of assembler code for function train_step: 0x00007687ba88b0f9 <+0>: endbr64 0x00007687ba88b0fd <+4>: push rbp 0x00007687ba88b0fe <+5>: mov rbp,rsp 0x00007687ba88b101 <+8>: mov rax,QWORD PTR [rip+0x2ed0] # 0x7687ba88dfd8 0x00007687ba88b108 <+15>: mov rax,QWORD PTR [rax] 0x00007687ba88b10b <+18>: lea rdx,[rax+0x1] 0x00007687ba88b10f <+22>: mov rax,QWORD PTR [rip+0x2ec2] # 0x7687ba88dfd8 0x00007687ba88b116 <+29>: mov QWORD PTR [rax],rdx 0x00007687ba88b119 <+32>: nop 0x00007687ba88b11a <+33>: pop rbp 0x00007687ba88b11b <+34>: ret End of assembler dump. (gdb) print/x **(unsigned long long **)0x7687ba88dfd8 $1 = 0xdeadbeefdeadbeef リロケーションとグローバル変数 param のアドレスが書き込まれた !
  22. 27 • Linux におけるプログラムの起動プロセス ◦ グローバル変数とは何か ◦ Linux におけるプログラムの起動の流れ ▪

    リロケーションとグローバル変数 • MN-Core コンパイラ・ランタイム ◦ MN-Core コンパイラ・ランタイムにおける グローバル変数とは何か ◦ プログラムの起動の流れ ▪ リロケーションとグローバル変数 • まとめ 目次
  23. 28 • PFN が開発している 機械学習用の ASIC ◦ デバイス DRAM と

    計算する部分がある※ MN-Core # MN-Core 機械語の例 # # この命令列は例示を目的としたもので # 意味がありません。 # オレンジの部分は計算をするための命令 # 緑の部分はDRAMにアクセスするための命令 l2bmb $lc64 $lb64; wait i02 l1bmm $llb0 $nowrite mvb/n64i01 $d10000016 $lc0 hbfe/9 $lbf $nowrite; l1bmm $llb32 $nowrite hmwrite $aluf $llx0; hbfe/9 $lbf $nowrite mvb/n64i02 $d10000032 $lc64 hmwrite $aluf $llx8; hbfe/9 $lm0v $nowrite hmmulr $lx $aluf $ln0v/1000 mvnop ※ 説明のために単純化しています
  24. 29 • 機械学習アプリケーションを MN-Core 上で動かすためのソフトウェア ◦ コンパイラ: PyTorch もしくは JAX

    を MN-Core 機械語に変換する ▪ 入力は PyTorch で書かれた関数 ▪ 出力は MN-Core 機械語とメタ情報 ▪ 今回はあまり解説しないが非常に大きい ◦ ランタイム: 変換した機械語を MN-Core で実行し結果を得る ▪ コンパイラの出力を受け取って Python から使えるようにする ▪ デバイスロック、リロケーション、メモリ管理 MN-Core コンパイラ・ランタイム
  25. 30 model = Model() optimizer = MomentumSGD(model.parameters()) def train_step(inputs): optimizer.zero_grad()

    loss = model(**inputs) loss.backward() optimizer.step() return {"loss": loss} def eval(inputs): return {"loss": model(**inputs)} sample = get_sample_input() compiled_train_step = compile(train_step, sample) compiled_eval = compile(eval_step, sample) for _ in range(3): compiled_train_step(sample) compiled_eval_step(sample) MN-Core で動く PyTorch のサンプルプログラム
  26. 31 model = Model() optimizer = MomentumSGD(model.parameters()) def train_step(inputs): optimizer.zero_grad()

    loss = model(**inputs) loss.backward() optimizer.step() return {"loss": loss} def eval(inputs): return {"loss": model(**inputs)} sample = get_sample_input() compiled_train_step = compile(train_step, sample) compiled_eval = compile(eval,    sample) for _ in range(3): compiled_train_step(sample) compiled_eval_step(sample) MN-Core で動く PyTorch のサンプルプログラム train_step は model のパラメータを更新する。 パラメータとは model の持つ状態だと考えてよい。 eval は model の持つパラメータを参照する。 train_step と eval は分けてコンパイルされる。 つまり、MN-Core 機械語のファイルが二つできる。
  27. 32 • モデル (torch.nn.Module) のパラメータは MN-Core コンパイラ・ランタイムにおけるグローバル変数である ◦ 同じ変数が複数の機械語ファイルに出現する ◦

    プログラム起動までアドレスが決定しない • モデル (torch.nn.Module) のパラメータを MN-Core コンパイラ・ランタイムはどのように扱うのか ? MN-Core コンパイラ・ランタイムにおける グローバル変数
  28. 33 • Linux におけるプログラムの起動プロセス ◦ グローバル変数とは何か ◦ Linux におけるプログラムの起動の流れ ▪

    リロケーションとグローバル変数 • MN-Core コンパイラ・ランタイム ◦ MN-Core コンパイラ・ランタイムにおける グローバル変数とは何か ◦ プログラムの起動の流れ ▪ リロケーションとグローバル変数 • まとめ 目次
  29. 34 1. コンパイラが Python の関数をコンパイルし、 MN-Core 機械語とメタ情報を出力する 2. ランタイムがメタ情報を読んで、 必要であればデバイスメモリの割り付けを行う

    a. モデルのパラメータ(≒ グローバル変数)のメモリ割付もここで行う 3. 割り付けたデバイスメモリメモリのアドレスに合うように MN-Core 機械語 を書き換える(= リロケーション) 4. 実行する MN-Core コンパイラ・ランタイムにおける プログラムの起動の流れ
  30. 35 • MN-Core コンパイラが MN-Core 機械語とメタ情報を出力する ◦ MN-Core 機械語のファイルサイズは非常に大きくなり得る ◦

    メタ情報には以下のような情報が含まれる ▪ MN-Core のバージョン ▪ リロケーション情報 ▪ 利用するデバイスメモリの大きさ ▪ デバッグ情報 コンパイル
  31. 36 • コンパイラが出力するメタ情報は、MN-Core 機械語がどれだけのデバイス DRAM を利用するかとそれぞれの デバイス DRAM の領域が PyTorch

    のど の変数に対応するかの情報を含んでいる • ランタイムはそのメタ情報を読んで、未割当のデバイス DRAM があれば割り 付ける ◦ 割り付け時はサイズだけを見るため、コンパイラが出力する MN-Core 機 械語とはアドレスが一致しないことがある デバイスメモリの割り付け inputs: x: DRAM@10000000-10000016/Half(1,10) outputs: y: DRAM@10000048-10000064/Half(1,1) params: spec: [email protected]: DRAM@10000016-10000032/Half(1,10) spec: [email protected]: DRAM@10000032-10000048/Float(1)
  32. 37 • 各変数に割り付けたデバイス DRAM のアドレスに一致するように MN-Core 機械語ファイルを書き換 える • メタ情報には

    MN-Core 機械語ファ イルのどの位置が PyTorch のどの 変数に対応するかの情報が含まれ ている ◦ R_X86_64_64 と意味は同じ リロケーション reloc: group name: [email protected] group addr: 10000032 group size: 16 value name:[email protected] value addr:10000032 value size:16 value bin_indices: [0, ] value packed_instruction_indices: [15, ] value addresses_in_group: [0, ] group name: [email protected] group addr: 10000016 group size: 16 value name:[email protected] value addr:10000016 value size:16 value bin_indices: [0, ] value packed_instruction_indices: [14, ] value addresses_in_group: [0, ]
  33. 38 • PCIe から MN-Core 機械語ファイル を流し込むと計算ができる ◦ MN-Coreランタイム -

    MN-Coreを動かすソフトウェア - Preferred Networks Research & Development ◦ 実際には実行とリロケーションはほぼ同時に行われるため、 命令中の DRAM アドレスを書き換えながら実行している MN-Core 機械語ファイルの実行
  34. 39 model = Model() optimizer = MomentumSGD(model.parameters()) def train_step(inputs): optimizer.zero_grad()

    loss = model(**inputs) loss.backward() optimizer.step() return {"loss": loss} def eval(inputs): return {"loss": model(**inputs)} sample = get_sample_input() compiled_train_step = compile(train_step, sample) compiled_eval = compile(eval,    sample) for _ in range(3): compiled_train_step(sample) compiled_eval_step(sample) [再掲] MN-Core で動く PyTorch のサンプルプログラム train_step は model のパラメータを更新する。 パラメータとは model の持つ状態だと考えてよい。 eval は model の持つパラメータを参照する。 train_step と eval は分けてコンパイルされる。 つまり、MN-Core 機械語のファイルが二つできる。
  35. 40 • train_step と eval をコンパイルすると MN-Core 機械語ファイルとメタ情報のファイルが二組得られる • train_step

    を実行する際に model のパラメータが デバイス DRAM 上に割り付けられる • eval を実行する際は既に割り付けられた model のパラメータに 合うようにリロケーションをする モデル (torch.nn.Module) のパラメータを MN-Core コンパイラ・ランタイムはどのように扱うのか
  36. 41 • Linux におけるプログラムの起動プロセス ◦ グローバル変数とは何か ◦ Linux におけるプログラムの起動の流れ ▪

    リロケーションとグローバル変数 • MN-Core コンパイラ・ランタイム ◦ MN-Core コンパイラ・ランタイムにおける グローバル変数とは何か ◦ プログラムの起動の流れ ▪ リロケーションとグローバル変数 • まとめ 目次
  37. 44 • リロケーションがなかったころの Linux の共有ライブラリの問題点 [1] ◦ The main accepted

    limitation was that no relocations are performed at the time of loading and afterward. The shared libraries have to exist in the form they are used at run-time on disk. This imposes a major restriction on the way shared libraries are built and used: every shared library must have a fixed load address; otherwise it would not be possible to generate shared libraries which do not have to be relocated. • 全ての共有ライブラリのアドレスを事前に決定する必要がある ◦ find / | grep “^.*.so$” して見つかる大量の共有ライブラリ全て のアドレスを管理する必要があり、事実上不可能 [1]: How To Write Shared Libraries by Ulrich Drepper そもそもリロケーションは必要なのか ? (Linux 編)
  38. 46 • パラメータを関数の入出力としてあつかうという設計もあり得る ◦ JAX の例 パラメータをグローバル変数にする必要はあるのか ? • しかしパラメータは非常に大きい場合に、実行時のオーバーヘッドが大きくな

    るのでグローバル変数にしている @jit def update(i, opt_state, batch): params = get_params(opt_state) return opt_update(i, grad(loss)(params, batch), opt_state) _, init_params = init_random_params(rng, (-1, 28 * 28)) opt_state = opt_init(init_params)