Slide 1

Slide 1 text

logica X: @logica0419 GitHub: @logica0419 標準ライブラリの 奥深アップデートを 掘り下げよう!

Slide 2

Slide 2 text

自己紹介 ● logica (ろじか) ● 千葉工業大学 情報科学部 情報ネットワーク学科 3年 ● ネットワークコンテンツ研究会 所属 ○ 数人の自宅サーバーをVPNで繋いでクラウド基盤 作ろうとしてます ● 最近、GoLabというイタリアで行われる カンファレンスでの登壇が決まりました

Slide 3

Slide 3 text

Go 1.23だ!!!

Slide 4

Slide 4 text

Go 1.23だ!!! イテレータ(range over func)だ!

Slide 5

Slide 5 text

Go 1.23だ!!! イテレータ(range over func)だ!

Slide 6

Slide 6 text

ところで…

Slide 7

Slide 7 text

ところで…

Slide 8

Slide 8 text

ところで…

Slide 9

Slide 9 text

ところで…

Slide 10

Slide 10 text

全部の変更 説明できる人?

Slide 11

Slide 11 text

説明できる方は 素晴らしい!

Slide 12

Slide 12 text

今回のアプデ、難しい ● そもそも需要が少ない ○ 問題意識を持つ人が少ない部分へのアプローチ ○ 特定の課題を解決する上では非常に強力 ● 理解するために、事前知識が必要 ○ 特にuniqueとstructsパッケージ ○ メモリに関する結構深めの知識が必須 でも、理解できるときっと面白い!

Slide 13

Slide 13 text

今日は 前提知識からしっかり 理解することを目指します! 頑張っていきましょう!

Slide 14

Slide 14 text

timeの重要な変更 (time.Timer と time.Ticker)

Slide 15

Slide 15 text

time.Timer / time.Tickerおさらい ● time.Timer ○ 1回のイベントを表す ○ 指定した時間が過ぎると、Timer.Cから現在時刻が 送られる ● time.Ticker ○ Timerの繰り返し版 ○ 指定した間隔で、Ticker.Cから現在時刻が送られ 続ける type Timer struct { C <-chan Time } type Ticker struct { C <-chan Time }

Slide 16

Slide 16 text

timeの重要な変更 (time.Timer と time.Ticker) 1. Stop()が呼ばれなくてもGCされる

Slide 17

Slide 17 text

そもそもGCとは?を超簡単に ● プログラムの基本的なデータフローはバケツリレー ○ 関数から関数に引数と戻り値で受け渡す ● メモリ内をヒープ・スタックという2つの領域に分けて データを管理する ○ バケツリレーはスタック ○ バケツリレーで共有しない ものはヒープ ■ ポインタで取り出す

Slide 18

Slide 18 text

ヒープ・スタックの使い分けとGC ● スタックだけだと不合理な場合がある ○ 大きなサイズの構造体は、バケツリレーだと コピーのコストが大きい などなど… ● Garbage Collection (GC) ○ スタックのデータは関数と寿命が一致するが、 ヒープは溜まる一方 ○ 使い終わったヒープのデータを検出・削除する プロセスがGC

Slide 19

Slide 19 text

来年のGo Conferenceの プロポーザルで詳しい話を 出したいと思っています… 応援してください

Slide 20

Slide 20 text

● 今までのTimer / Tickerは、以下の条件だと使われ ないことが分かっていてもGCされなかった ○ Timer: Stop()されてない || 時間が過ぎてない ○ Ticker: Stop()されてない ● ↑ のため、関数内でしか使わないTimer / Tickerを 生成したときはdefer Stop()する必要があった ● Go 1.23からはしなくても良くなります! Stop()が呼ばれなくてもGCされる

Slide 21

Slide 21 text

timeの重要な変更 (time.Timer と time.Ticker) 1. Stop()が呼ばれなくてもGCされる 2. channelがバッファ0に

Slide 22

Slide 22 text

Timer / Tickerのchannel ● 今までは1のバッファがあった ○ Stop()やReset()を呼んだ後でも、呼び出し前に 入れられた値は取り出せてしまう ○ これがStop()やReset()の使用を難しくしていた ● これからはバッファが0になる ○ channelから値をすぐに出さなくてもTimer / Tickerが狂うとかはない ○ Stop()やReset()を呼ぶと値は取り出せなくなる

Slide 23

Slide 23 text

go.modとasynctimerchan=1 ● 今回の挙動が適用されるのは、go.modのgoの行の バージョンが1.23.0以降の時だけ ○ 古いバージョンを書いていたら、コンパイラが 新しくても古い挙動が適用される ● GODEBUG=asynctimerchan=1をつけてビルドすると 古い挙動が適用される(channelもGCも) ○ Go 1.27までは使える予定らしい

Slide 24

Slide 24 text

uniqueの追加

Slide 25

Slide 25 text

uniqueって何? ● なんかsliceから重複した値消し去ってくれるとか? ● stringの中の同じ文字検知してくれるとか? → 全然違います! uniqueパッケージは、interningという かなり攻めたメモリ使用量の削減手法を Goに実装したもの です。

Slide 26

Slide 26 text

interning?

Slide 27

Slide 27 text

これはinternship

Slide 28

Slide 28 text

interningとは ● 以下のプログラムを考えてみよう func main() { values := make([]int, 2) values[0] = 10 values[1] = 10 fmt.Println( values[1], values[2], ) }

Slide 29

Slide 29 text

interningとは ● 以下のプログラムを考えてみよう func main() { values := make([]int, 2) values[0] = 10 values[1] = 10 fmt.Println( values[1], values[2], ) } メモリ使用イメージ

Slide 30

Slide 30 text

interningとは ● 以下のプログラムを考えてみよう func main() { values := make([]int, 2) values[0] = 10 values[1] = 10 fmt.Println( values[1], values[2], ) } メモリ使用イメージ (同じ値が複数あるの 無駄なのでは…?)

Slide 31

Slide 31 text

interningとは これを

Slide 32

Slide 32 text

interningとは これを  こうしたい

Slide 33

Slide 33 text

interningとは これを  こうしたい 1つの値にまとめて 容量を削減

Slide 34

Slide 34 text

interningとは これを  こうしたい 1つの値にまとめて 容量を削減 これがinterning

Slide 35

Slide 35 text

uniqueパッケージ ● Handle ○ interningを実装した型 ○ 実態はTのポインタだと思えばOK ○ Value()メソッドで実際の値を引き出せる ● Make() ○ Handle型を生成するための関数 ○ もし過去に同じvalueでHandle型が生成されて いたら、その時のポインタを流用する type Handle[T comparable] struct {} func (h Handle[T]) Value() T func Make[T comparable](value T) Handle[T]

Slide 36

Slide 36 text

使い方 ● さっきのプログラムを書き換えてみる func main() { values := make( []unique.Handle[int], 2 ) values[0] = unique.Make(10) values[1] = unique.Make(10) fmt.Println( values[1].Value(), values[2].Value(), ) } func main() { values := make( []int, 2 ) values[0] = 10 values[1] = 10 fmt.Println( values[1], values[2], ) }

Slide 37

Slide 37 text

使い方 ● さっきのプログラムを書き換えてみる func main() { values := make( []unique.Handle[int], 2 ) values[0] = unique.Make(10) values[1] = unique.Make(10) fmt.Println( values[1].Value(), values[2].Value(), ) } メモリ使用イメージ

Slide 38

Slide 38 text

stringへの特殊な挙動(string interning) ● stringは「変更不可能なポインタ」と呼ばれる ○ ポインタだが、その実体が変更不可 ○ unsafe.StringData()でアドレスを取得可能 ● stringに対してinterningを行うと、このアドレスが 同じ実体に対して揃う ○ 「string interning」 と呼ばれる

Slide 39

Slide 39 text

stringへの特殊な挙動(string interning) ● 本来interningとstring interningはちょっと違う概念 ○ interning: 本来はオブジェクトが対象 ○ string interning: 文字列型限定 ● 今回のuniqueパッケージは、実際は interningとstring interningを同じインターフェース で使えるようにしたもの と言えるかな…?という感じ ○ Pythonとかも一緒(というかPythonは自動でやる)

Slide 40

Slide 40 text

使いどころ ● 正直そう無いかもしれないが、効くところには効く ○ 同じ値を色んな箇所で何度も使う場合 ○ ↑ 特に値がstringや容量が大きい構造体の場合 ● net/netipの例 - IPアドレスのプロパティをinterning type Addr struct { addr uint128 z unique.Handle[addrDetail] } type addrDetail struct { isV6 bool // IPv4 is false, IPv6 is true. zoneV6 string // != "" only if IsV6 is true. }

Slide 41

Slide 41 text

structsの追加

Slide 42

Slide 42 text

structsって何? ● structに対して便利な機能を提供してくれる ユーティリティーだと思ってた! → 全然違います! structsパッケージは、structに関して、 メモリレイアウトのような言語仕様に関わる プロパティを変更できるようにするものです。

Slide 43

Slide 43 text

structsパッケージ type HostLayout struct {} ● 今回追加された変更可能プロパティは1つ ● HostLayout ○ 埋め込まれた構造体のメモリレイアウトが、ホスト OSのC言語ABIに則ることを保証する ● 使い方 type Example struct { _ structs.HostLayout // こんな感じで差し込むだけ Value string }

Slide 44

Slide 44 text

ABI? メモリレイアウト? なんじゃそりゃ? みんな「?」だと思うので、1つずつ確認しましょう

Slide 45

Slide 45 text

ABI ● Application Binary Interface (ABI) ○ OSとバイナリ / バイナリ同士の約束ごと ○ バイナリの中で変数がどのようにメモリに入るか / 関数がどのように呼ばれるか などを規定する ○ CGOで特に重要(Goから直にCバイナリを呼ぶので) ● 参考: Application Programming Interface (API) ○ アプリケーション同士の約束事 ○ どんな機能を持ち、どんな風に使うかを定義する

Slide 46

Slide 46 text

ABI ● Goは独自のABIを持っている ○ https://go.googlesource.com/go/+/refs/heads/ dev.regabi/src/cmd/compile/internal-abi.md ● Cも、コンパイラごと(GNUとかmuslとか)にABIを 持っている ○ なので、ホストOSごとにCのABIは異なる ○ 「ホストOSのC言語ABI」はこのこと

Slide 47

Slide 47 text

メモリレイアウト ● ABIの中で、特に変数がどんな風にメモリに入るかを 定義している部分 ○ 各型のバイト数やアライン、 structのメモリへの積み方 などが書いてある ● Goの場合の例 ○ 型のバイト数は→ ○ structはフィールド順

Slide 48

Slide 48 text

改めてstructsパッケージ type HostLayout struct {} ● 今回追加された変更可能プロパティは1つ ● HostLayout ○ 埋め込まれた構造体のメモリレイアウトが、ホスト OSのC言語ABIに則ることを保証する ● 使い方 type Example struct { _ structs.HostLayout // こんな感じで差し込むだけ Value string }

Slide 49

Slide 49 text

なぜstructsパッケージが必要なのか ● 元々Goのメモリレイアウトの実装は、ホストOSの C言語ABIに強く依存している ○ CGOを使うとき、ホストOSのC言語ABIと揃って いないと壊れる可能性がある ←これ、稀では? ● structsパッケージがあると、メモリレイアウトを 崩したとき壊れるstructを明示できるため、メモリ レイアウトを崩すメモリ最適化ができる! ○ structをフィールド順に従わず配置する など

Slide 50

Slide 50 text

すなわち、HostLayoutは 付けた物を変えるんじゃなく 付けない物を変える下準備です! これだけ皆覚えて帰って下さい

Slide 51

Slide 51 text

まとめ と 宣伝

Slide 52

Slide 52 text

「自分使わないな」って機能も 深堀りすると面白いって 思ってもらえたら幸いです! 超スピードで駆け抜けてしまって申し訳ないです…

Slide 53

Slide 53 text

“Go Far”s Japan プロジェクト (仮) ● 僕の人脈・ノウハウ・やる気全てを動員して、日本の Gopherたちを海外カンファレンスに引き込んでいく プロジェクトをやりたいと思っています! ○ Gophers Japanと協力してやりたいですがまだ 企画書を書いていないです ● やりたいこと ○ 国内英語 / 日本語同時発表カンファレンス ○ プロポーザルのクオリティUP支援 などなど!

Slide 54

Slide 54 text

ありがとう ございました

Slide 55

Slide 55 text

参考文献たち ● https://go.dev/doc/go1.23 ● https://pkg.go.dev/ ● https://pythontutor.com/ ● https://github.com/golang/go/issues/62483 ● https://future-architect.github.io/articles/20240719a/ ● https://ikorin2.hatenablog.jp/entry/2019/12/16/195847 ● https://qiita.com/kahibella/items/cd8b48f1d2c109b76e10 ● https://speakerdeck.com/ymotongpoo/memory-manageme nt-in-go ● https://cs.opensource.google/go/go/+/refs/tags/go1.23. 0:src/net/netip/netip.go ● https://github.com/golang/go/issues/66408 ● https://satoru-takeuchi.hatenablog.com/entry/2020/03/ 26/011858