Source file
src/runtime/mksizeclasses.go
Documentation: runtime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package main
31
32 import (
33 "bytes"
34 "flag"
35 "fmt"
36 "go/format"
37 "io"
38 "log"
39 "math"
40 "math/bits"
41 "os"
42 )
43
44
45
46 var stdout = flag.Bool("stdout", false, "write to stdout instead of sizeclasses.go")
47
48 func main() {
49 flag.Parse()
50
51 var b bytes.Buffer
52 fmt.Fprintln(&b, "// Code generated by mksizeclasses.go; DO NOT EDIT.")
53 fmt.Fprintln(&b, "//go:generate go run mksizeclasses.go")
54 fmt.Fprintln(&b)
55 fmt.Fprintln(&b, "package runtime")
56 classes := makeClasses()
57
58 printComment(&b, classes)
59
60 printClasses(&b, classes)
61
62 out, err := format.Source(b.Bytes())
63 if err != nil {
64 log.Fatal(err)
65 }
66 if *stdout {
67 _, err = os.Stdout.Write(out)
68 } else {
69 err = os.WriteFile("sizeclasses.go", out, 0666)
70 }
71 if err != nil {
72 log.Fatal(err)
73 }
74 }
75
76 const (
77
78 minHeapAlign = 8
79 maxSmallSize = 32 << 10
80 smallSizeDiv = 8
81 smallSizeMax = 1024
82 largeSizeDiv = 128
83 pageShift = 13
84
85
86 pageSize = 1 << pageShift
87 )
88
89 type class struct {
90 size int
91 npages int
92 }
93
94 func powerOfTwo(x int) bool {
95 return x != 0 && x&(x-1) == 0
96 }
97
98 func makeClasses() []class {
99 var classes []class
100
101 classes = append(classes, class{})
102
103 align := minHeapAlign
104 for size := align; size <= maxSmallSize; size += align {
105 if powerOfTwo(size) {
106 if size >= 2048 {
107 align = 256
108 } else if size >= 128 {
109 align = size / 8
110 } else if size >= 32 {
111 align = 16
112 }
113 }
114 if !powerOfTwo(align) {
115 panic("incorrect alignment")
116 }
117
118
119
120
121 allocsize := pageSize
122 for allocsize%size > allocsize/8 {
123 allocsize += pageSize
124 }
125 npages := allocsize / pageSize
126
127
128
129
130
131
132 if len(classes) > 1 && npages == classes[len(classes)-1].npages && allocsize/size == allocsize/classes[len(classes)-1].size {
133 classes[len(classes)-1].size = size
134 continue
135 }
136 classes = append(classes, class{size: size, npages: npages})
137 }
138
139
140
141
142
143
144
145 for i := range classes {
146 if i == 0 {
147 continue
148 }
149 c := &classes[i]
150 psize := c.npages * pageSize
151 new_size := (psize / (psize / c.size)) &^ (largeSizeDiv - 1)
152 if new_size > c.size {
153 c.size = new_size
154 }
155 }
156
157 if len(classes) != 68 {
158 panic("number of size classes has changed")
159 }
160
161 for i := range classes {
162 computeDivMagic(&classes[i])
163 }
164
165 return classes
166 }
167
168
169
170
171
172 func computeDivMagic(c *class) {
173
174 d := c.size
175 if d == 0 {
176 return
177 }
178
179
180 max := c.npages * pageSize
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204 N := bits.Len(uint(max))
205 var F int
206 if powerOfTwo(d) {
207 F = int(math.Log2(float64(d)))
208 if d != 1<<F {
209 panic("imprecise log2")
210 }
211 } else {
212 for L := 0; ; L++ {
213 if d <= ((1<<(N+L))%d)+(1<<L) {
214 F = N + L
215 break
216 }
217 }
218 }
219
220
221
222
223 if F > 32 {
224 fmt.Printf("d=%d max=%d N=%d F=%d\n", c.size, max, N, F)
225 panic("size class requires more than 32 bits of precision")
226 }
227
228
229
230 m := ^uint32(0)/uint32(c.size) + 1
231 for n := 0; n <= max; n++ {
232 if uint32((uint64(n)*uint64(m))>>32) != uint32(n/c.size) {
233 fmt.Printf("d=%d max=%d m=%d n=%d\n", d, max, m, n)
234 panic("bad 32-bit multiply magic")
235 }
236 }
237 }
238
239 func printComment(w io.Writer, classes []class) {
240 fmt.Fprintf(w, "// %-5s %-9s %-10s %-7s %-10s %-9s %-9s\n", "class", "bytes/obj", "bytes/span", "objects", "tail waste", "max waste", "min align")
241 prevSize := 0
242 var minAligns [pageShift + 1]int
243 for i, c := range classes {
244 if i == 0 {
245 continue
246 }
247 spanSize := c.npages * pageSize
248 objects := spanSize / c.size
249 tailWaste := spanSize - c.size*(spanSize/c.size)
250 maxWaste := float64((c.size-prevSize-1)*objects+tailWaste) / float64(spanSize)
251 alignBits := bits.TrailingZeros(uint(c.size))
252 if alignBits > pageShift {
253
254 alignBits = pageShift
255 }
256 for i := range minAligns {
257 if i > alignBits {
258 minAligns[i] = 0
259 } else if minAligns[i] == 0 {
260 minAligns[i] = c.size
261 }
262 }
263 prevSize = c.size
264 fmt.Fprintf(w, "// %5d %9d %10d %7d %10d %8.2f%% %9d\n", i, c.size, spanSize, objects, tailWaste, 100*maxWaste, 1<<alignBits)
265 }
266 fmt.Fprintf(w, "\n")
267
268 fmt.Fprintf(w, "// %-9s %-4s %-12s\n", "alignment", "bits", "min obj size")
269 for bits, size := range minAligns {
270 if size == 0 {
271 break
272 }
273 if bits+1 < len(minAligns) && size == minAligns[bits+1] {
274 continue
275 }
276 fmt.Fprintf(w, "// %9d %4d %12d\n", 1<<bits, bits, size)
277 }
278 fmt.Fprintf(w, "\n")
279 }
280
281 func maxObjsPerSpan(classes []class) int {
282 most := 0
283 for _, c := range classes[1:] {
284 n := c.npages * pageSize / c.size
285 most = max(most, n)
286 }
287 return most
288 }
289
290 func printClasses(w io.Writer, classes []class) {
291 fmt.Fprintln(w, "const (")
292 fmt.Fprintf(w, "minHeapAlign = %d\n", minHeapAlign)
293 fmt.Fprintf(w, "_MaxSmallSize = %d\n", maxSmallSize)
294 fmt.Fprintf(w, "smallSizeDiv = %d\n", smallSizeDiv)
295 fmt.Fprintf(w, "smallSizeMax = %d\n", smallSizeMax)
296 fmt.Fprintf(w, "largeSizeDiv = %d\n", largeSizeDiv)
297 fmt.Fprintf(w, "_NumSizeClasses = %d\n", len(classes))
298 fmt.Fprintf(w, "_PageShift = %d\n", pageShift)
299 fmt.Fprintf(w, "maxObjsPerSpan = %d\n", maxObjsPerSpan(classes))
300 fmt.Fprintln(w, ")")
301
302 fmt.Fprint(w, "var class_to_size = [_NumSizeClasses]uint16 {")
303 for _, c := range classes {
304 fmt.Fprintf(w, "%d,", c.size)
305 }
306 fmt.Fprintln(w, "}")
307
308 fmt.Fprint(w, "var class_to_allocnpages = [_NumSizeClasses]uint8 {")
309 for _, c := range classes {
310 fmt.Fprintf(w, "%d,", c.npages)
311 }
312 fmt.Fprintln(w, "}")
313
314 fmt.Fprint(w, "var class_to_divmagic = [_NumSizeClasses]uint32 {")
315 for _, c := range classes {
316 if c.size == 0 {
317 fmt.Fprintf(w, "0,")
318 continue
319 }
320 fmt.Fprintf(w, "^uint32(0)/%d+1,", c.size)
321 }
322 fmt.Fprintln(w, "}")
323
324
325 sc := make([]int, smallSizeMax/smallSizeDiv+1)
326 for i := range sc {
327 size := i * smallSizeDiv
328 for j, c := range classes {
329 if c.size >= size {
330 sc[i] = j
331 break
332 }
333 }
334 }
335 fmt.Fprint(w, "var size_to_class8 = [smallSizeMax/smallSizeDiv+1]uint8 {")
336 for _, v := range sc {
337 fmt.Fprintf(w, "%d,", v)
338 }
339 fmt.Fprintln(w, "}")
340
341
342 sc = make([]int, (maxSmallSize-smallSizeMax)/largeSizeDiv+1)
343 for i := range sc {
344 size := smallSizeMax + i*largeSizeDiv
345 for j, c := range classes {
346 if c.size >= size {
347 sc[i] = j
348 break
349 }
350 }
351 }
352 fmt.Fprint(w, "var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv+1]uint8 {")
353 for _, v := range sc {
354 fmt.Fprintf(w, "%d,", v)
355 }
356 fmt.Fprintln(w, "}")
357 }
358
View as plain text