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
R8 の設定見直しでアプリサイズ削減
andpad
0
180
技術的負債の返済は、AI時代の複利で効く投資 — 経営としての意思決定とその遂行
curekoshimizu
1
2.2k
なぜCTOを降りてFDEを選んだのか?〜なぜプロダクト企業がFDEで顧客の現場に踏み込むのか〜
gonta
1
160
Java 27新機能 / Java 27 new features
kishida
2
200
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
1.1k
The Rails Doctrine Decade
koic
2
510
RAG の “R” を Swift で覗いてみる 〜「意味から探す」検索の仕組み〜
nao_randd
0
120
すこし踏み込む CancellationToken
htkym
2
1.6k
CodeRabbitの効果検証と過ごしてみた3ヶ月
armondando
0
150
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
270
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
370
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
450
Featured
See All Featured
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
2
930
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.6k
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
310
The Cult of Friendly URLs
andyhume
79
7k
Typedesign – Prime Four
hannesfritz
42
3.2k
Chasing Engaging Ingredients in Design
codingconduct
0
340
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
590
Everyday Curiosity
cassininazir
0
350
How to make the Groovebox
asonas
2
2.5k
The Pragmatic Product Professional
lauravandoore
37
7.5k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
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の処理が メモリ確保を高速化するため、クラスで定められ たサイズまでメモリ確保量が切り上げされている からである。
おわり