Slide 1

Slide 1 text

GoのMapとHeapを自作してみ た @DQNEO (どきゅねお) 2019/2/25 mercari.go

Slide 2

Slide 2 text

コンパイラ をゼロから自作してます。 https://github.com/DQNEO/minigo

Slide 3

Slide 3 text

が動くようになった! / https://twitter.com/DQNEO/status/1088422242772381697

Slide 4

Slide 4 text

どうやって作ったのか

Slide 5

Slide 5 text

● ● GoのMap おさらい ● ● ●

Slide 6

Slide 6 text

GoのMap Key: 任意の型 Value: 任意の型 要素数:任意

Slide 7

Slide 7 text

GoのMap 任意の型の と 任意の型の を 任意の数だけ格納できるナニカ

Slide 8

Slide 8 text

どこから手を付ければ

Slide 9

Slide 9 text

自作コンパイラ3原則 小さくはじめる 動けば正義 遅くても気にしない

Slide 10

Slide 10 text

小さくはじめる

Slide 11

Slide 11 text

↓ とにかくハードルを下げる まずは 型は int のみ

Slide 12

Slide 12 text

m = map [int]int { 1: 2, 3: 4, } とにかくハードルを下げる 要素数も固定

Slide 13

Slide 13 text

とにかくハードルを下げる 1 2 3 4 メモリ上に数値が並んでるだけ

Slide 14

Slide 14 text

これなら作れそう!

Slide 15

Slide 15 text

m = map [int]int { 1: 2, 3: 4, } map[int]int 1 2 3 4 メモリ上に8byteずつ値を並べる

Slide 16

Slide 16 text

x = m[3] map get 1 2 3 4 keyを16byteずつホップしながら探索 マッチしたらその右隣の値を返す

Slide 17

Slide 17 text

Keyの探索 (X86-64の例) emit("mov %%r13, %%rax") // i emit("imul $16, %%rax") // i * 16 emit("mov %%r10, %%rcx") // head emit("add %%rax, %%rcx") // head + i * 16 emit("mov (%%rcx), %%rdx") // eval key value 16byteずつホップしながらkeyを探索

Slide 18

Slide 18 text

マッチしたKey位置から Valueを取得 emit("mov 8(%%rcx), %%r15") // get value of value emit("jmp %s", labelEnd) key値がマッチしたら、 8bytes隣のvalue値を取り出して 探索ループを脱出する

Slide 19

Slide 19 text

map get 実装の詳細はこちら https://github.com/DQNEO/minigo/commit/9f0cef573 d772d67a0efc8fa88c78b185743ea40#diff-fa95711d 3528c54a10a4bd5a3303bf8cR1443

Slide 20

Slide 20 text

map set 前述のようにKeyの探索を行う Keyが存在する? Yes -> その位置+8bytesの場所に値を書き込む。 No -> データ領域の末尾にKey,Valueを追記。(後述)

Slide 21

Slide 21 text

for range構文 for k,v := range m { ... }

Slide 22

Slide 22 text

for range構文 for k,v := range m { ... } for i:=0, i

Slide 23

Slide 23 text

を作る

Slide 24

Slide 24 text

を作る に追記するには動的なメモリ確保 が必要 を実装するのはチョットむずい 疑似 でお茶を濁す

Slide 25

Slide 25 text

疑似 静的グローバル 配列をheapに見 立て、そこからメ モリを必要なだけ 切り取る

Slide 26

Slide 26 text

疑似 の利点 メモリ開始アドレスが固定なので デバグしやすい

Slide 27

Slide 27 text

これで への 要素追加が可能に

Slide 28

Slide 28 text

↓ 型を汎用化したい

Slide 29

Slide 29 text

1 "hello" 3 "world" valueのサイズが可変 → むずい

Slide 30

Slide 30 text

1 &s1 3 &s2 1 &i1 3 &i2 値のアドレスを格納する 値自体はheapに置き、そのアドレスをmap領域に書 き込む。 valueのサイズは8bytesのままなので、今までの mapロジックをほぼそのまま使える

Slide 31

Slide 31 text

mov (%rax), %rax Valueのデリファレンス 値を取り出した直後に1度デリファレンスするだけ

Slide 32

Slide 32 text

"hello" "world" "こんにち わ" "世界" keyの型も汎用にしたい

Slide 33

Slide 33 text

&s1 &s2 &s3 &s4 keyも同様にアドレスを登録する

Slide 34

Slide 34 text

これで動く が作れた

Slide 35

Slide 35 text

の実装全体はこんな感じ https://github.com/DQNEO/minigo/compare/3 654b7c283a01efd273a336188fd13ab10dbdf6 e...3f6ce0773dd9bd7a233b2278f8911a5253f 90999

Slide 36

Slide 36 text

おまけ 簡単そうに言っているが、 動的アドレス計算を駆使するので、 一歩間違えると Segmentation fault 地獄

Slide 37

Slide 37 text

おかげで gdb が使えるようになりました おまけ

Slide 38

Slide 38 text

おまけ このmapの仕組みを使えば、"interface"機能も実 現できる。(できた)