Go 1.26でのsliceのメモリアロケーション最適化 / Go 1.26 リリースパーティ #go126party
by
mazrean
×
Copy
Open
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Go 1.26でのsliceのメモリアロケーション最適化 @mazrean Go 1.26 リリースパーティ
Slide 2
Slide 2 text
mazrean ■ Goでツール等を作っている ● DIツールKessoku ● SQL Builder GenORM ■ SRE @DeNA @mazrean22 マズリーン 2
Slide 3
Slide 3 text
今日の話題 Sliceアロケーション改善
Slide 4
Slide 4 text
Release Noteになくない? と思った人もいるかも
Slide 5
Slide 5 text
状況に応じ、スタックにSliceを確保する高速化を追加
Slide 6
Slide 6 text
目次 6 1 スタック・ヒープとSlice 2 1.26でのSliceアロケーションの変化 3 変化のトレードオフ 4 まとめ
Slide 7
Slide 7 text
目次 7 1 スタック・ヒープと Slice 2 1.26でのSliceアロケーションの変化 3 変化のトレードオフ 4 まとめ
Slide 8
Slide 8 text
1.26のSliceアロケーション改善 スタック・ヒープ の使い方が改善 ■ ヒープ確保 が多発する場合があった ● 十分ありうるケース ■ 1.26で確保回数が大幅に減少 ● 最大3割減 8
Slide 9
Slide 9 text
スタック・ヒープ 多くの言語ではメモリを2領域に分けて管理 ■ スタック: 一時的なデータの保存先 ■ ヒープ: 長期的なデータの保存先 9
Slide 10
Slide 10 text
スタック 関数内でのみ使用される変数の保存先 ■ 関数 return 時に解放 ■ 確保・解放が高速 10 スタック func main() x
Slide 11
Slide 11 text
スタック 関数内でのみ使用される変数の保存先 ■ 関数 return 時に解放 ■ 確保・解放が高速 11 スタック func main() x func a() y 確保
Slide 12
Slide 12 text
スタック 関数内でのみ使用される変数の保存先 ■ 関数 return 時に解放 ■ 確保・解放が高速 12 スタック func main() x func a() y func b() z 確保
Slide 13
Slide 13 text
スタック 関数内でのみ使用される変数の保存先 ■ 関数 return 時に解放 ■ 確保・解放が高速 13 スタック func main() x func a() y func b() z 解放
Slide 14
Slide 14 text
スタック 関数内でのみ使用される変数の保存先 ■ 関数 return 時に解放 ■ 確保・解放が高速 14 スタック func main() x func a() y 解放
Slide 15
Slide 15 text
ヒープ 関数外でも使用される変数の保存先 ■ ポインタがなくなった後に解放 ● Garbage Collection ■ 確保・解放が遅い 15 ヒープ スタック func main() func a() y func b() z 関数外参照
Slide 16
Slide 16 text
エスケープ解析 基本的にコンパイラ がヒープに置く変数決定 ■ 関数外参照 の可能性がある場合、ヒープ ■ -gcflags=”-m”で確認可能 16
Slide 17
Slide 17 text
Sliceのメモリ配置 ヘッダーが配列のポインタを保持 ■ ヘッダーは他にlen・capを持つ 17 配列 ヘッダー len cap
Slide 18
Slide 18 text
Sliceのappend capを超えたら新しい配列確保(growslice) ■ データがコピーされる ■ 大量のメモリ確保 が発生 18 growslice
Slide 19
Slide 19 text
目次 19 1 スタック・ヒープとSlice 2 1.26でのSliceアロケーションの変化 3 変化のトレードオフ 4 まとめ
Slide 20
Slide 20 text
1.26 以前 関数外参照ありSliceは最初からヒープに確保 20 ヒープ スタック func main() func a()
Slide 21
Slide 21 text
問題点 append連続時にヒープ確保 が繰り返される ■ growsliceが連続 ■ 速度低下につながる 21 ヒープ スタック func main() func a() growslice growslice
Slide 22
Slide 22 text
1.26以降 Sliceの配列を最初はスタックに確保 ■ 必要ならreturn時にヒープへコピー ■ ヒープ確保 が大幅減 22 ヒープ スタック func main() func a() growslice コピー
Slide 23
Slide 23 text
ベンチマーク ■ []intに1,000,000回append ■ 1.25.6と1.26.0で比較 ■ 環境 ● CPU: AMD Ryzen 9 7950X ● メモリ: DDR5 24GB x 2 23 コード:https://gist.github.com/mazrean/bd336266c955ee52ed62e3cbec48f251
Slide 24
Slide 24 text
ベンチマーク(capなし) 速度向上 ■ アロケーション回数減少 ■ 約20%実行時間削減 24 実行時間 アロケーション回数 1.25.6 10,655,058 ns 40 1.26.0 8,219,547 ns 38
Slide 25
Slide 25 text
目次 25 1 スタック・ヒープとSlice 2 1.26でのSliceアロケーションの変化 3 変化のトレードオフ 4 まとめ
Slide 26
Slide 26 text
常に速くなるのか? cap付き確保の場合、オーバーヘッド が入る ■ growsliceが発生しない ■ ヒープコピー のみ追加 26 ヒープ スタック func main() func a() コピー
Slide 27
Slide 27 text
ベンチマーク(capあり) ほんの少し遅くなる ■ 改善と比べたら極めて小さい ● 2,000,000ns改善⇔100,000ns悪化 ■ cap付きの方が速いのは変わらず 27 実行時間 アロケーション回数 1.25.6 534,803 ns 1 1.26.0 646,919 ns 1
Slide 28
Slide 28 text
目次 28 1 スタック・ヒープとSlice 2 1.26でのSliceアロケーションの変化 3 変化のトレードオフ 4 まとめ
Slide 29
Slide 29 text
まとめ ■ スライスのメモリ確保最適化 が入った ● ヒープ確保の削減 ■ 多くの場合で速度向上 ■ トレードオフ を考慮して導入されている ● 僅かに速度低下する場合もある ● cap付きの方が速いのは変わらず 29
Slide 30
Slide 30 text
余談: コード中ドキュメント コードコメントで丁寧な説明がされている ■ Release Noteに書いてほしかった… 30 https://cs.opensource.google/go/go/+/master:src/cmd/compile/internal/slice/slice.go;l=7-111?q= slice.go&ss=go%2Fgo
Slide 31
Slide 31 text
宣伝1(個人): Go Proposal Weekly Digest Proposalを追うためのサイト作っています ■ RSSもあります ■ ステータス変動 の経緯が追える 31 https://go-proposal-weekly-digest.mazrean.com/
Slide 32
Slide 32 text
宣伝2(会社): DeNA.go ~ 1.26 Deep Dive ~ Go 1.26をワイワイ話して 深掘る(Not LT) ■ Release Note見て議論・深掘り をする ● 初心者歓迎 ■ 明日開催 32 https://dena.connpass.com/event/381352/
Slide 33
Slide 33 text
宣伝3(会社): Go Junction DeNA・Cyber Agent・DMMのGo同LT会 ■ DeNAからは以下2つの発表 ● IRIAMのギフト機能の開発・運用の裏側 ● ミューテーションテストツール ■ 残り枠僅か! 33 https://cyberagent.connpass.com/event/381653/