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
スライス容量拡張量がどのように決まるのか追った / 180709 LT
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
kaznishi
July 09, 2018
Programming
190
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
スライス容量拡張量がどのように決まるのか追った / 180709 LT
kaznishi
July 09, 2018
More Decks by kaznishi
See All by kaznishi
Finally_I_can_kichijojipm32
kaznishi
0
700
バッチ処理と冪等性 / 20191218_merpay_techtalk
kaznishi
3
5.6k
Bounds Check Eliminationについて調べてみた / 1218-lt
kaznishi
0
590
Hello, Prometheus!! Goで作るexporter自作入門 / 180727 LT
kaznishi
6
3.9k
Goのスライス容量拡張量がどのように決まるのか追った / 180713 LT
kaznishi
3
3.8k
Other Decks in Programming
See All in Programming
AIが無かった頃の素敵な出会いの話
codmoninc
1
380
琵琶湖の水は止められてもNet--HTTPのリトライは止められない / You might be able to stop the water flow of Lake Biwa but you can't stop Net::HTTP retries
luccafort
PRO
0
550
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
470
Claude Opus 4.6以後の受託開発エンジニアの変化(Claude Code開発ノウハウ大公開スペシャルbyクラスメソッド)
iidatakuma
1
960
AWS DevOps AgentのAzure接続機能を検証して見えた活用法/Use Cases Verified for the AWS DevOps Agent's Azure Connectivity Feature
masakiokuda
1
210
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
1.6k
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
140
使用 Meilisearch 建立新聞搜尋工具
johnroyer
0
210
komatsuna「分散システムにおけるバグ分析手法」
komatsunaqa
0
230
Embedded SREと共に達成した会員管理システムのAWS移行 - SRE NEXT 2026 ランチスポンサーセッション
niftycorp
PRO
1
3.4k
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
210
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
460
Featured
See All Featured
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
570
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
119
120k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.7k
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
72
41k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
480
Build your cross-platform service in a week with App Engine
jlugia
234
19k
How to make the Groovebox
asonas
2
2.3k
Navigating Weather and Climate Data
rabernat
0
440
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.4k
Visualization
eitanlees
152
17k
BBQ
matthewcrist
89
10k
Transcript
※(7/13追記)ブラッシュアップした内容で2018/7/13 のgolang.tokyoでLTしてきたので、そちらのスライド の方をご覧ください! https://speakerdeck.com/kaznishi/180713-lt
スライス容量拡張量が どのように決まるのか追った 2018-07-09 Gopher道場 #2 LT大会 by kaznishi
自己紹介 twitter: @kaznishi1246 サーバーサイド,インフラ PHP, Scala Go歴はほぼ1ヶ月(≒Gopher道場期間)
今回のテーマ
スライスの容量がいっぱいの ときに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 なんだろうこれは、という感じ
「Goならわかるシステムプログラミング」より 小さなオブジェクトについては、より小さな単位 の「クラス」という分類で空きメモリのリストを 持っています。クラスからのメモリ取得では、リ クエストされたサイズに近いクラスの空きリスト があればそこからメモリを確保します。この場合 にはロックが不要であり、それだけ高速に処理で きます。 “ “
クラスという分類で空きメモ リリストを管理するため、キ リの良いところまで切り上げ をしている
意味が掴めたところでnewcapの計算 結果を確かめてみる
再掲
old.cap = 5 Go Playground環境においては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)
計算してみたところ、ちゃんと結果が12になりまし た。 https://play.golang.org/p/oSn8GbSWVkK
まとめ スライスの容量拡張される際の新容量は、元容量 の大体2倍である。 (今回の話から省いたが、スラ イス長が大きい場合(1024が閾値)は大体1.25倍) きっちり2倍にならないのは、メモリ管理上キリの よいところまでメモリ確保量が切り上げされてい るからである。
None