Go 1.26でのsliceのメモリアロケーション最適化 / Go 1.26 リリースパーティ #go126party
by
mazrean
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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/