Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Goのスライス容量拡張量がどのように決まるのか追った / 180713 LT
Search
kaznishi
July 13, 2018
Programming
3.8k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Goのスライス容量拡張量がどのように決まるのか追った / 180713 LT
kaznishi
July 13, 2018
More Decks by kaznishi
See All by kaznishi
Finally_I_can_kichijojipm32
kaznishi
0
690
バッチ処理と冪等性 / 20191218_merpay_techtalk
kaznishi
2
5.6k
Bounds Check Eliminationについて調べてみた / 1218-lt
kaznishi
0
580
Hello, Prometheus!! Goで作るexporter自作入門 / 180727 LT
kaznishi
6
3.9k
スライス容量拡張量がどのように決まるのか追った / 180709 LT
kaznishi
0
190
Other Decks in Programming
See All in Programming
PHPで使える日時の表現と、その知り方 #frontend_phpcon_do
o0h
PRO
0
260
Snowflake Summitでの新機能 CoCo / CoWork / snowflake-summit-2026-overall-what-new-coco
tatsuhiro
1
170
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
2
700
TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通知基盤 / TSKaigi Night talks ~after conference~
d0riven
0
360
過去最大のMCPアップデート! 2026-07-28 RC版の謎に迫る
licux
6
390
Semantic Version 単位で戦略を柔軟に変えて、パッケージアップデートを自動化する
daitasu
1
300
「AIで開発し、AIを届ける」をEvalでつなぐ 〜AIネイティブに始めるプロダクト開発の実践〜 / Connecting "Develop with AI, deliver AI" with Eval
rkaga
4
5.4k
エンジニア向け会社紹介/Findy Company Profile
findyinc
6
350k
そのテスト、説明できますか?~LWテスト戦略FW~のご紹介
nakahara
0
160
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
280
その問い、本当に正しいですか?AI時代のエンジニアに必要な哲学と認知科学 / ai-philosophy-cognitive-science
minodriven
13
6.2k
エンジニアと一緒にテストコードの設計と実装を改善した話
mototakatsu
0
220
Featured
See All Featured
Odyssey Design
rkendrick25
PRO
2
700
Java REST API Framework Comparison - PWX 2021
mraible
34
9.4k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
620
The agentic SEO stack - context over prompts
schlessera
0
820
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
170
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
So, you think you're a good person
axbom
PRO
2
2.1k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
1
230
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
370
We Are The Robots
honzajavorek
0
250
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.3k
Transcript
Goのスライス容量拡張量が どのように決まるのか追った 2018-07-13 golang.tokyo #16 LT by kaznishi
自己紹介 Twitter: @kaznishi1246 主な守備範囲: サーバーサイド,インフラ 主な使用言語: PHP, Scala Go歴: 約1ヶ月
(≒Gopher道場#2)
今回のテーマ
スライスが満容量のときに appendで追加される容量の話
復習 スライスは配列の部分列への参照のためのデータ 構造 配列は固定長 容量が足りなくなった場合、容量が拡張された新 たな配列が作られ、参照先が切り替わる
容量の拡張量は?
容量の拡張量は? 「プログラミング言語Go」より 「Goならわかるシステムプログラミング」より 拡張ごとに配列の大きさを倍にすることにより過 剰な回数の割り当てを避け、一つの要素の追加が 平均的に定数時間で済むことを保証しています。 “ “ もし、余裕がない状態でappend()を呼ぶと、cap() の2倍のメモリを確保し、今までの要素をコピー
したうえで新しい要素を新しいメモリ領域に追加 します。 “ “
確かめてみよう
Go Playgroundで確認 cap = 4 のとき https://play.golang.org/p/zPLWMUM2gzw OK
Go Playgroundで確認 cap = 5 のとき https://play.golang.org/p/eyPOVocDUc- 「12」!!!???
はて
Goの実装を追ってみた
https://github.com/golang/go/blob/master/src/runti me/slice.go func growslice(et *_type, old slice, cap int) slice
{ ~略~ newcap := old.cap doublecap := newcap + newcap if cap > doublecap { newcap = cap } else { if old.len < 1024 { newcap = doublecap } else { // Check 0 < newcap to detect overflow // and prevent an infinite loop. for 0 < newcap && newcap < cap { newcap += newcap / 4 } ~略~
~略~ switch { case et.size == 1: lenmem = uintptr(old.len)
newlenmem = uintptr(cap) capmem = roundupsize(uintptr(newcap)) overflow = uintptr(newcap) > maxAlloc newcap = int(capmem) case et.size == sys.PtrSize: lenmem = uintptr(old.len) * sys.PtrSize newlenmem = uintptr(cap) * sys.PtrSize capmem = roundupsize(uintptr(newcap) * sys.PtrSize) overflow = uintptr(newcap) > maxAlloc/sys.PtrSize newcap = int(capmem / sys.PtrSize)
case isPowerOfTwo(et.size): var shift uintptr if sys.PtrSize == 8 {
// Mask shift for better code generation. shift = uintptr(sys.Ctz64(uint64(et.size))) & } else { shift = uintptr(sys.Ctz32(uint32(et.size))) & } lenmem = uintptr(old.len) << shift newlenmem = uintptr(cap) << shift capmem = roundupsize(uintptr(newcap) << shift) overflow = uintptr(newcap) > (maxAlloc >> shift) newcap = int(capmem >> shift) default: lenmem = uintptr(old.len) * et.size newlenmem = uintptr(cap) * et.size capmem = roundupsize(uintptr(newcap) * et.size) overflow = uintptr(newcap) > maxSliceCap(et.size) newcap = int(capmem / et.size) }
newcapに調整がかかってる
switch { case et.size == sys.PtrSize: ~略~ capmem = roundupsize(uintptr(newcap)
* sys.PtrSize) ~略~ newcap = int(capmem / sys.PtrSize) default: ~略~ capmem = roundupsize(uintptr(newcap) * et.size) ~略~ newcap = int(capmem / et.size) } 確保メモリ = roundupsize(補正前スライス容量 x 要素サイズ) 補正後スライス容量 = 確保メモリ / 要素サイズ
roundupsize?
https://github.com/golang/go/blob/master/src/runti me/msize.go func roundupsize(size uintptr) uintptr { if size <
_MaxSmallSize { if size <= smallSizeMax-8 { return uintptr(class_to_size[size_to_class8 [(size+smallSizeDiv-1)/smallSizeDiv]]) } else { return uintptr(class_to_size[size_to_class128 [(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv]]) } } if size+_PageSize < size { return size } return round(size, _PageSize) }
roundupsizeというからにはメモリの確保量を切り 上げているのだろうが、何のための切り上げ? class_to_size , size_to_class の'class'とは?
https://github.com/golang/go/blob/master/src/runti me/sizeclasses.go // class bytes/obj bytes/span objects tail waste max
waste // 1 8 8192 1024 0 87.50% // 2 16 8192 512 0 43.75% // 3 32 8192 256 0 46.88% // 4 48 8192 170 32 31.52% // ... ... .... ... 〜略〜 var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 32, 48, 64, ... 〜略〜 var size_to_class8 = [smallSizeMax/smallSizeDiv + 1]uint8{0, 1, 2, 3, 3, 4, 4,.. これは一体…?
「Goならわかるシステムプログラミング」より 小さなオブジェクトについては、より小さな単位 の「クラス」という分類で空きメモリのリストを 持っています。クラスからのメモリ取得では、リ クエストされたサイズに近いクラスの空きリスト があればそこからメモリを確保します。この場合 にはロックが不要であり、それだけ高速に処理で きます。 “ “
https://github.com/golang/go/blob/master/src/runti me/malloc.go のコメントより 1. Round the size up to one
of the small size classes and look in the corresponding mspan in this P's mcache. Scan the mspan's free bitmap to nd a free slot. If there is a free slot, allocate it. This can all be done without acquiring a lock. “ “
TCMalloc実装の一部分 TCMallocについては下記URLに詳細 http://goog- perftools.sourceforge.net/doc/tcmalloc.html ただしGoにおけるTCMallocはGo向けにアレンジ されたもの。(malloc.goのコメントによると "This was originally based
on tcmalloc, but has diverged quite a bit." とのことです。)
roundupsizeにおけるclassとは サイズをレンジごとに分類するもの 分類されたクラスごとに空きメモリリストを持つ 空きメモリリストからのメモリ確保は高速な処理 が可能
意味が掴めたところでnewcapの計算 結果を確かめてみる
再掲
old.cap = 5 Go Playground環境(GOOS=NaCl, GOARCH=amd64p32)においてはintのet.sizeは4, sys.PtrSizeも4 newcap := old.cap
doublecap := newcap + newcap ~略~ if old.len < 1024 { newcap = doublecap } else { ~略~ case et.size == sys.PtrSize: ~略~ capmem = roundupsize(uintptr(newcap) * sys.PtrSize) ~略~ newcap = int(capmem / sys.PtrSize)
拡張前容量 * 2 = 5 * 2 = 10 ↓
roundupsize前のcapmem = 10 * 4 = 40(bytes) ↓ class 4 (33~48 bytes) に分類 ↓ roundupsizeで 48 bytes に切り上げ ↓ 新しいスライス容量 = 48 / 4 = 12 計算結果が12であることを確認! https://play.golang.org/p/oSn8GbSWVkK
まとめ スライスの容量拡張される際の新容量は、元容量 の大体2倍である。 (今回の話から省いたが、スラ イス長が大きい場合(1024が閾値)は大体1.25倍) きっちり2倍にならないのは、TCMallocの処理が メモリ確保を高速化するため、クラスで定められ たサイズまでメモリ確保量が切り上げされている からである。
おわり