Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
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
720
バッチ処理と冪等性 / 20191218_merpay_techtalk
kaznishi
3
5.7k
Bounds Check Eliminationについて調べてみた / 1218-lt
kaznishi
0
600
Hello, Prometheus!! Goで作るexporter自作入門 / 180727 LT
kaznishi
6
3.9k
スライス容量拡張量がどのように決まるのか追った / 180709 LT
kaznishi
0
190
Other Decks in Programming
See All in Programming
Intent as Code
shoppingjaws
6
1k
【高い買い物LT会】初任給で話題の国産フィジカルAIを買った話
akagami
PRO
0
160
テストを司るデーモンに会いに行く 〜隔離した仮想マシンでテストを通すまで〜
h1d3mun3
1
350
大喜利で理解するLLM as a Judge / Understanding LLM-as-a-Judge through Ogiri
rockname
0
100
一参加者から『中の人』へ 〜全通PHPerがブースに立って学んだ、カンファレンスを100倍楽しむコツ〜
wp_daisuke
0
150
XP祭りでしか伝わらないフリップネタ #xpjug
murabayashi
0
150
20260914 AIエージェント時代のPlatform Engineering LLM基盤とプロダクトの責務境界線
kanfab1
6
1.9k
個人開発基盤をまるごとCloudflareに引っ越して爆速で総合的体験を向上させた話
tinykitten
0
180
From 6 People Classroom Meetup to 100 People Regional Conference / FOSS4G Hiroshima 2026
furukawayasuto
0
200
技術的負債を組織課題として解く-増えすぎたマイクロサービスとの戦い-
reimaru
1
1.8k
A2UI for Android: Safely Rendering AI-Generated UI with Jetpack Compose - DroidKaigi 2026
itsmedreamwalker
0
140
型解析で実現する Go の言語内 DSL / Conference に Go! タイムテーブルの歩き方 for Gophers
mazrean
0
150
Featured
See All Featured
Six Lessons from altMBA
skipperchong
29
4.5k
Balancing Empowerment & Direction
lara
6
1.3k
From π to Pie charts
rasagy
1
370
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
480
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.6k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
250
1.3M
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
690
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
18k
Become a Pro
speakerdeck
PRO
31
6.3k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
850
The Mindset for Success: Future Career Progression
greggifford
PRO
0
490
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.6k
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の処理が メモリ確保を高速化するため、クラスで定められ たサイズまでメモリ確保量が切り上げされている からである。
おわり