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

バイナリを眺めてわかる gob encoding の仕様と性質、適切な使い方 / understanding gob encoding

convto
June 07, 2024

バイナリを眺めてわかる gob encoding の仕様と性質、適切な使い方 / understanding gob encoding

Go Conference 2024 の発表資料
Room1 15:50~

convto

June 07, 2024
Tweet

More Decks by convto

Other Decks in Programming

Transcript

  1. gob ってなに? - データのエンコーディングのひとつ! - 有名なのは json, xml, protobuf(wire), など...

    - Go が標準パッケージで実装してる独自のエンコーディング!
  2. gob ってなに? - データのエンコーディングのひとつ! - 有名なのは json, xml, protobuf(wire), など...

    - Go が標準パッケージで実装してる独自のエンコーディング! なんで独自のものが 必要だったの?
  3. Protobuf で学んだこととは? - Go では struct 定義でしか動作しないこと - proto2 required

    は後方/前方互換担保を難しくした - required 実装を追加すると server/client 間で互換が壊れる - proto2 では default を設定できたが、実装や挙動が複雑になる - type ごとのゼロ値のような概念の方が取り回しやすい
  4. Protobuf で学んだこととは? - Go では struct 定義でしか動作しないこと - proto2 required

    は後方/前方互換担保を難しくした - required 実装を追加すると server/client 間で互換が壊れる - proto2 では default を設定できたが、実装や挙動が複雑になる - type ごとのゼロ値のような概念の方が取り回しやすい proto3 で解決されてるのでちょっと古い
  5. 現代的な観点でみたときの良いところ - Goのプログラム上から特別な宣言なしに利用できる - バイナリエンコーディングで情報の転送効率がよいこと - self-describing であること - struct

    必須など, 利用する encoding によって構造の持ち方が制限さ れない これらを全て満たすエンコーディングはない!ので作った
  6. 今回は以下の観点で gob を評価 - パフォーマンス - サイズ - 後方/前方互換性 -

    自己言及的 (self-describing) であるか - エコシステム - etc …
  7. 雰囲気で感じ取れること - ぱっとみ型情報をゴリっと送ってそう - 同一 stream 上だと最初の一回しか型を送ってなさそう - 明らかに終端っぽいやつがいそう -

    フィールド名とかを構造としてベタっと送ってるので、Protobuf (wire) み たいに field num 同じなら名前変えても無問題!とはならなさそう
  8. cnt: 14 cnt: 36 id: -64 wireType cnt: 1 id:

    64 wireType value こんな感じそう
  9. cnt: 14 cnt: 36 id: -64 wireType cnt: 1 id:

    64 wireType value cnt: 13 cnt: 1 id: 64 value こんな感じそう
  10. こんな感じそう cnt: 14 cnt: 36 id: -64 wireType cnt: 1

    id: 64 wireType value cnt: 13 cnt: 1 id: 64 wireType と value の詳細がわかれば勝てそう value
  11. cnt: 36 id: 64 wireType 初期値 -1 で delta encoding

    されると 仕様で言及されてる debug 実装もそうなってる
  12. cnt: 36 id: 64 wireType 0b11 = 3 delta encoding

    初期値 -1 を考えて, field_number: 2 を表現 structType である! type def なら以下みたいな構造で パースされる(gob pkg で定義)
  13. cnt: 36 id: 64 wireType 今回は StructT や! 0b11 =

    3 delta encoding 初期値 -1 を考えて, field_number: 2 を表現 structType である!
  14. cnt: 36 id: 64 wireType 今回は StructT や! 0b11 =

    3 delta encoding 初期値 -1 を考えて, field_number: 2 を表現 structType である! debug 実装も 2 なら struct として食べ てる
  15. cnt: 36 id: -64 wireType struct type はこんな感じ 0b11 =

    3 delta encoding 初期値 -1 を考えて, field_number: 2 を表現 structType である!
  16. cnt: 36 id: -64 wireType 説明のために簡略化してまとめるとこ んな感じ! 0b11 = 3

    delta encoding 初期値 -1 を考えて, field_number: 2 を表現 structType である!
  17. cnt: 36 id: -64 wireType 説明のために簡略化してまとめるとこ んな感じ! 0b11 = 3

    delta encoding 初期値 -1 を考えて, field_number: 2 を表現 structType である! いまここ!
  18. cnt: 36 id: -64 wireType ← の 1byte から 大まかな構造が

    わかる! 説明のために簡略化してまとめるとこ んな感じ! いまここ!
  19. cnt: 36 id: -64 wireType 0b1 = 1 delta encoding

    初期値 -1 を考えて, field_number: 0 説明のために簡略化してまとめるとこ んな感じ! いまここ!
  20. cnt: 36 id: -64 wireType 0b1 = 1 delta encoding

    初期値 -1 を考えて, field_number: 0 説明のために簡略化してまとめるとこ んな感じ! いまここ!
  21. cnt: 36 id: -64 wireType 0b100 = 4 len: 4

    string はよくある len が先に来て そのあと val があるパターン debug 実装
  22. cnt: 36 id: -64 wireType 眼力で `item` とわかる string はよくある

    len が先に来て そのあと val があるパターン debug 実装
  23. cnt: 36 id: -64 wireType 眼力で `item` とわかる ほんとはこれがある →

    string はよくある len が先に来て そのあと val があるパターン debug 実装
  24. cnt: 36 id: -64 wireType 次は id 0b1 = 1

    delta encoding 初期値 -1, 直前が0なので, field_number: 1
  25. cnt: 36 id: -64 wireType 次は id いまここ! 0b1 =

    1 delta encoding 初期値 -1, 直前が0なので, field_number: 1
  26. cnt: 36 id: -64 wireType 取り出して先頭bitが立ってなければ val 返す 立ってたら符号反転したものが len

    数値も len のあと val っぽい ちょっと癖ある 先頭bitが立ってるので len を表す int8(uint8(0xff)) = -1 反転して 1 len: 1
  27. cnt: 36 id: -64 wireType とった len で値を読む (複数桁あったらいい感じに加算) int

    だったら最下位bitが立ってたら反転 zigzag encoding ぽいかんじ
  28. cnt: 36 id: -64 wireType 1bit 右shift 末尾が立ってないからそ のまま 0b01000000

    = 64 id: 64 とった len で値を読む (複数桁あったらいい感じに加算) int だったら最下位bitが立ってたら反転 zigzag encoding ぽいかんじ
  29. cnt: 36 id: -64 wireType 1bit 右shift 末尾が立ってないからそ のまま 0b01000000

    = 64 id: 64 とった len で値を読む (複数桁あったらいい感じに加算) int だったら最下位bitが立ってたら反転 zigzag encoding ぽいかんじ gob の数値は全部これ これで飛ばしてた 箇所も読める
  30. cnt: 36 id: -64 wireType つぎは field 解決ずみ ✅ 0b1

    = 1 delta encoding 初期値 -1, 直前が0なので, field_number: 1
  31. cnt: 36 id: -64 wireType つぎは field 解決ずみ ✅ 0x10

    = 2 先頭立ってないから そのまま値 len: 2
  32. cnt: 36 id: -64 wireType つぎは field 解決ずみ ✅ 0b1

    = 1 delta encoding 初期値 -1 を考慮して field_number: 0
  33. cnt: 36 id: -64 wireType つぎは field 解決ずみ ✅ 解決ずみ

    ✅ 0b1 = 1 delta encoding 初期値 -1, 直前が0なので, field_number: 1
  34. cnt: 36 id: -64 wireType つぎは field 解決ずみ ✅ 解決ずみ

    ✅ 0b1100 = 6 先頭立ってないから 値, 末尾0で反転なし val: 6
  35. cnt: 36 id: -64 wireType ふたつめの field ! (サボります) 解決ずみ

    ✅ 解決ずみ ✅ Name = Price Id = 4 終端 👍
  36. cnt: 14 cnt: 1 id: 64 value 余談: さっき skip

    したこまいところもいまは読める 先頭立ってるから len, 符号反転して len: 1
  37. cnt: 14 cnt: 1 id: 64 value 1bit 右 shift

    して 0b01000000 末尾0だから 64 余談: さっき skip したこまいところもいまは読める
  38. cnt: 14 cnt: 1 id: 64 value id: 64 の構造は

    これ 構造をみていく ようはこれがわかればよい
  39. cnt: 14 cnt: 1 id: 64 value id: 64 の構造は

    これ id が負数でないので val で解釈 debug 実装
  40. cnt: 14 cnt: 1 id: 64 value id: 64 の構造は

    これ wireType によってちょっと挙動がかわる debug 実装
  41. cnt: 14 cnt: 1 id: 64 value id: 64 の構造は

    これ wireType によってちょっと挙動がかわる こっちを通る
  42. cnt: 14 cnt: 1 id: 64 value Name field 0b1

    = 1 delta encoding 初期値 -1 を考慮して field_number: 0
  43. cnt: 14 cnt: 1 id: 64 value Price field 0b1

    = 1 delta encoding 初期値 -1, 直前0を考慮して field_number: 1
  44. cnt: 14 cnt: 1 id: 64 value Price field 先頭bitが立ってるので

    len を表す int8(uint8(0xff)) = -1 反転して 1 len: 1
  45. cnt: 14 cnt: 1 id: 64 value Price field 1bit

    右shift 末尾が立ってないからそ のまま 0b01100100 = 100 val: 100
  46. cnt: 14 cnt: 1 id: 64 value Price field 1bit

    右shift 末尾が立ってないからそ のまま 0b01100100 = 100 val: 100 👍
  47. 今回は以下の観点で gob を評価 - パフォーマンス - サイズ - 後方/前方互換性 -

    自己言及的 (self-describing) であるか - エコシステム - etc …
  48. 今回は以下の観点で gob を評価 性質 \ encoding gob json protobuf (wire)

    サイズ 前方/後方互換性 自己言及的か エコシステム
  49. サイズはこんな感じ 性質 \ encoding gob json protobuf (wire) サイズ 🔺

    stream で上手 く使えば小さい ❌ 比較的大きい ⭕ 小さい 前方/後方互換性 自己言及的か エコシステム
  50. フィールド名を変えると値が取れなくなる - のでおおよそ互換性については json と同じ性質を持っていそう - ただ gob はアプリケーション側の型の差分についてはわりといい感じにし てくれる

    - ポインタあるなしとか、フィールドなかったら単に無視とか - protobuf(wire) と違って具体的なフィールド名などが露出するので、命 名変えた時とかは気をつけないと過去のメッセージから値を取り出せない
  51. 互換性 性質 \ encoding gob json protobuf (wire) サイズ 🔺

    stream で上手 く使えば小さい ❌ 比較的大きい ⭕ 小さい 前方/後方互換性 🔺 Go の型は多少 うまくしてくれる ❌ なし ⭕ filed_num の み露出 自己言及的か エコシステム
  52. 自己言及的か 性質 \ encoding gob json protobuf (wire) サイズ 🔺

    stream で上手 く使えば小さい ❌ 比較的大きい ⭕ 小さい 前方/後方互換性 🔺 Go の型は多少 うまくしてくれる ❌ なし ⭕ filed_num の み露出 自己言及的か ⭕⭕ 完全な型情 報を持ち実装の高 度なサポートあり 🔺~ ⭕ 構造の情 報あり 🔺 6種類くらいの 大まかな種別あり エコシステム
  53. エコシステムは ...? - json はどこでも使える - protobuf(wire) は高度なエコシステムのサポートがある - 一方

    gob は現時点で Go からしか使えない! - 言語埋め込みだから実現できる高度な機能とのトレードオフ - 理屈上他言語にも実装できるから、どうしても困ったら最悪そうすればえ えかくらい
  54. こうなった! 性質 \ encoding gob json protobuf (wire) サイズ 🔺

    stream で上手 く使えば小さい ❌ 比較的大きい ⭕ 小さい 前方/後方互換性 🔺 Go の型は多少 うまくしてくれる ❌ なし ⭕ filed_num の み露出 自己言及的か ⭕⭕ 完全な型情 報を持ち実装の高 度なサポートあり 🔺~ ⭕ 構造の情 報あり 🔺 6種類くらいの 大まかな種別あり エコシステム ❌ Go だけ ⭕ どこでもOK ⭕ そこそこ使える し周辺ツールが発 展してる
  55. 性質 \ encoding gob json protobuf (wire) サイズ 🔺 stream

    で上手 く使えば小さい ❌ 比較的大きい ⭕ 小さい 前方/後方互換性 🔺 Go の型は多少 うまくしてくれる ❌ なし ⭕ filed_num の み露出 自己言及的か ⭕⭕ 完全な型情 報を持ち実装の高 度なサポートあり 🔺~ ⭕ 構造の情 報あり 🔺 6種類くらいの 大まかな種別あり エコシステム ❌ Go だけ ⭕ どこでもOK ⭕ そこそこ使える し周辺ツールが発 展してる gob はどういうときに使えるか
  56. 性質 \ encoding gob json protobuf (wire) サイズ 🔺 stream

    で上手 く使えば小さい ❌ 比較的大きい ⭕ 小さい 前方/後方互換性 🔺 Go の型は多少 うまくしてくれる ❌ なし ⭕ filed_num の み露出 自己言及的か ⭕⭕ 完全な型情 報を持ち実装の高 度なサポートあり 🔺~ ⭕ 構造の情 報あり 🔺 6種類くらいの 大まかな種別あり エコシステム ❌ Go だけ ⭕ どこでもOK ⭕ そこそこ使える し周辺ツールが発 展してる これが許容できて gob はどういうときに使えるか
  57. 性質 \ encoding gob json protobuf (wire) サイズ 🔺 stream

    で上手 く使えば小さい ❌ 比較的大きい ⭕ 小さい 前方/後方互換性 🔺 Go の型は多少 うまくしてくれる ❌ なし ⭕ filed_num の み露出 自己言及的か ⭕⭕ 完全な型情 報を持ち実装の高 度なサポートあり 🔺~ ⭕ 構造の情 報あり 🔺 6種類くらいの 大まかな種別あり エコシステム ❌ Go だけ ⭕ どこでもOK ⭕ そこそこ使える し周辺ツールが発 展してる この性質を強く 求めていて gob はどういうときに使えるか
  58. 性質 \ encoding gob json protobuf (wire) サイズ 🔺 stream

    で上手 く使えば小さい ❌ 比較的大きい ⭕ 小さい 前方/後方互換性 🔺 Go の型は多少 うまくしてくれる ❌ なし ⭕ filed_num の み露出 自己言及的か ⭕⭕ 完全な型情 報を持ち実装の高 度なサポートあり 🔺~ ⭕ 構造の情 報あり 🔺 6種類くらいの 大まかな種別あり エコシステム ❌ Go だけ ⭕ どこでもOK ⭕ そこそこ使える し周辺ツールが発 展してる かつ json, protobuf (wire) の中間みたいな  バランスが噛み合う用途 gob はどういうときに使えるか
  59. - Go でしか使えない制約を受け入れられるとき - 検証用途のコードで使うとか - データライフサイクルが短いもの - キャッシュとか -

    RPC やりとりの encoding として - 後述するメリットとバランスがとれるとき gob はどういうときに使えるか じつは標準の net/rpc と かでも使われてる
  60. gob の嬉しい性質 - Go で完結してすぐ使えて嬉しい - 大抵嬉しいと思う - Go でしか使えないこととのトレードオフという感じ

    - そこそこ効率がよかったり、まあまあメッセージの互換性が取れることが嬉しい - デメリットとバランスするなら採用余地あり!
  61. まとめ - みんなでバイナリを読んで gob を完全理解しました - 大体どれもこんな感じなんで興味あったら protobuf (wire) の

    spec も読ん でみてください - encoding 仕様からいくつかの性質について整理した - gob が使えそうなところや, 採用した時の嬉しさを整理した - キャッシュやRPCにおすすめ - Go から便利に使えること, その他の性質が程よいところがメリット - pros/cons 整理してバランスするならいい選択肢