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 maxSmallSize = 32 << 10
79 smallSizeDiv = 8
80 smallSizeMax = 1024
81 largeSizeDiv = 128
82 pageShift = 13
83
84
85 pageSize = 1 << pageShift
86 )
87
88 type class struct {
89 size int
90 npages int
91 }
92
93 func powerOfTwo(x int) bool {
94 return x != 0 && x&(x-1) == 0
95 }
96
97 func makeClasses() []class {
98 var classes []class
99
100 classes = append(classes, class{})
101
102 align := 8
103 for size := align; size <= maxSmallSize; size += align {
104 if powerOfTwo(size) {
105 if size >= 2048 {
106 align = 256
107 } else if size >= 128 {
108 align = size / 8
109 } else if size >= 32 {
110 align = 16
111 }
112 }
113 if !powerOfTwo(align) {
114 panic("incorrect alignment")
115 }
116
117
118
119
120 allocsize := pageSize
121 for allocsize%size > allocsize/8 {
122 allocsize += pageSize
123 }
124 npages := allocsize / pageSize
125
126
127
128
129
130
131 if len(classes) > 1 && npages == classes[len(classes)-1].npages && allocsize/size == allocsize/classes[len(classes)-1].size {
132 classes[len(classes)-1].size = size
133 continue
134 }
135 classes = append(classes, class{size: size, npages: npages})
136 }
137
138
139
140
141
142
143
144 for i := range classes {
145 if i == 0 {
146 continue
147 }
148 c := &classes[i]
149 psize := c.npages * pageSize
150 new_size := (psize / (psize / c.size)) &^ (largeSizeDiv - 1)
151 if new_size > c.size {
152 c.size = new_size
153 }
154 }
155
156 if len(classes) != 68 {
157 panic("number of size classes has changed")
158 }
159
160 for i := range classes {
161 computeDivMagic(&classes[i])
162 }
163
164 return classes
165 }
166
167
168
169
170
171 func computeDivMagic(c *class) {
172
173 d := c.size
174 if d == 0 {
175 return
176 }
177
178
179 max := c.npages * pageSize
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 N := bits.Len(uint(max))
204 var F int
205 if powerOfTwo(d) {
206 F = int(math.Log2(float64(d)))
207 if d != 1<<F {
208 panic("imprecise log2")
209 }
210 } else {
211 for L := 0; ; L++ {
212 if d <= ((1<<(N+L))%d)+(1<<L) {
213 F = N + L
214 break
215 }
216 }
217 }
218
219
220
221
222 if F > 32 {
223 fmt.Printf("d=%d max=%d N=%d F=%d\n", c.size, max, N, F)
224 panic("size class requires more than 32 bits of precision")
225 }
226
227
228
229 m := ^uint32(0)/uint32(c.size) + 1
230 for n := 0; n <= max; n++ {
231 if uint32((uint64(n)*uint64(m))>>32) != uint32(n/c.size) {
232 fmt.Printf("d=%d max=%d m=%d n=%d\n", d, max, m, n)
233 panic("bad 32-bit multiply magic")
234 }
235 }
236 }
237
238 func printComment(w io.Writer, classes []class) {
239 fmt.Fprintf(w, "// %-5s %-9s %-10s %-7s %-10s %-9s %-9s\n", "class", "bytes/obj", "bytes/span", "objects", "tail waste", "max waste", "min align")
240 prevSize := 0
241 var minAligns [pageShift + 1]int
242 for i, c := range classes {
243 if i == 0 {
244 continue
245 }
246 spanSize := c.npages * pageSize
247 objects := spanSize / c.size
248 tailWaste := spanSize - c.size*(spanSize/c.size)
249 maxWaste := float64((c.size-prevSize-1)*objects+tailWaste) / float64(spanSize)
250 alignBits := bits.TrailingZeros(uint(c.size))
251 if alignBits > pageShift {
252
253 alignBits = pageShift
254 }
255 for i := range minAligns {
256 if i > alignBits {
257 minAligns[i] = 0
258 } else if minAligns[i] == 0 {
259 minAligns[i] = c.size
260 }
261 }
262 prevSize = c.size
263 fmt.Fprintf(w, "// %5d %9d %10d %7d %10d %8.2f%% %9d\n", i, c.size, spanSize, objects, tailWaste, 100*maxWaste, 1<<alignBits)
264 }
265 fmt.Fprintf(w, "\n")
266
267 fmt.Fprintf(w, "// %-9s %-4s %-12s\n", "alignment", "bits", "min obj size")
268 for bits, size := range minAligns {
269 if size == 0 {
270 break
271 }
272 if bits+1 < len(minAligns) && size == minAligns[bits+1] {
273 continue
274 }
275 fmt.Fprintf(w, "// %9d %4d %12d\n", 1<<bits, bits, size)
276 }
277 fmt.Fprintf(w, "\n")
278 }
279
280 func maxObjsPerSpan(classes []class) int {
281 most := 0
282 for _, c := range classes[1:] {
283 n := c.npages * pageSize / c.size
284 most = max(most, n)
285 }
286 return most
287 }
288
289 func printClasses(w io.Writer, classes []class) {
290 fmt.Fprintln(w, "const (")
291 fmt.Fprintf(w, "_MaxSmallSize = %d\n", maxSmallSize)
292 fmt.Fprintf(w, "smallSizeDiv = %d\n", smallSizeDiv)
293 fmt.Fprintf(w, "smallSizeMax = %d\n", smallSizeMax)
294 fmt.Fprintf(w, "largeSizeDiv = %d\n", largeSizeDiv)
295 fmt.Fprintf(w, "_NumSizeClasses = %d\n", len(classes))
296 fmt.Fprintf(w, "_PageShift = %d\n", pageShift)
297 fmt.Fprintf(w, "maxObjsPerSpan = %d\n", maxObjsPerSpan(classes))
298 fmt.Fprintln(w, ")")
299
300 fmt.Fprint(w, "var class_to_size = [_NumSizeClasses]uint16 {")
301 for _, c := range classes {
302 fmt.Fprintf(w, "%d,", c.size)
303 }
304 fmt.Fprintln(w, "}")
305
306 fmt.Fprint(w, "var class_to_allocnpages = [_NumSizeClasses]uint8 {")
307 for _, c := range classes {
308 fmt.Fprintf(w, "%d,", c.npages)
309 }
310 fmt.Fprintln(w, "}")
311
312 fmt.Fprint(w, "var class_to_divmagic = [_NumSizeClasses]uint32 {")
313 for _, c := range classes {
314 if c.size == 0 {
315 fmt.Fprintf(w, "0,")
316 continue
317 }
318 fmt.Fprintf(w, "^uint32(0)/%d+1,", c.size)
319 }
320 fmt.Fprintln(w, "}")
321
322
323 sc := make([]int, smallSizeMax/smallSizeDiv+1)
324 for i := range sc {
325 size := i * smallSizeDiv
326 for j, c := range classes {
327 if c.size >= size {
328 sc[i] = j
329 break
330 }
331 }
332 }
333 fmt.Fprint(w, "var size_to_class8 = [smallSizeMax/smallSizeDiv+1]uint8 {")
334 for _, v := range sc {
335 fmt.Fprintf(w, "%d,", v)
336 }
337 fmt.Fprintln(w, "}")
338
339
340 sc = make([]int, (maxSmallSize-smallSizeMax)/largeSizeDiv+1)
341 for i := range sc {
342 size := smallSizeMax + i*largeSizeDiv
343 for j, c := range classes {
344 if c.size >= size {
345 sc[i] = j
346 break
347 }
348 }
349 }
350 fmt.Fprint(w, "var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv+1]uint8 {")
351 for _, v := range sc {
352 fmt.Fprintf(w, "%d,", v)
353 }
354 fmt.Fprintln(w, "}")
355 }
356
View as plain text