github.com/lzhfromustc/gofuzz@v0.0.0-20211116160056-151b3108bbd1/runtime/mksizeclasses.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build ignore 6 7 // Generate tables for small malloc size classes. 8 // 9 // See malloc.go for overview. 10 // 11 // The size classes are chosen so that rounding an allocation 12 // request up to the next size class wastes at most 12.5% (1.125x). 13 // 14 // Each size class has its own page count that gets allocated 15 // and chopped up when new objects of the size class are needed. 16 // That page count is chosen so that chopping up the run of 17 // pages into objects of the given size wastes at most 12.5% (1.125x) 18 // of the memory. It is not necessary that the cutoff here be 19 // the same as above. 20 // 21 // The two sources of waste multiply, so the worst possible case 22 // for the above constraints would be that allocations of some 23 // size might have a 26.6% (1.266x) overhead. 24 // In practice, only one of the wastes comes into play for a 25 // given size (sizes < 512 waste mainly on the round-up, 26 // sizes > 512 waste mainly on the page chopping). 27 // For really small sizes, alignment constraints force the 28 // overhead higher. 29 30 package main 31 32 import ( 33 "bytes" 34 "flag" 35 "fmt" 36 "go/format" 37 "io" 38 "log" 39 "os" 40 ) 41 42 // Generate msize.go 43 44 var stdout = flag.Bool("stdout", false, "write to stdout instead of sizeclasses.go") 45 46 func main() { 47 flag.Parse() 48 49 var b bytes.Buffer 50 fmt.Fprintln(&b, "// Code generated by mksizeclasses.go; DO NOT EDIT.") 51 fmt.Fprintln(&b, "//go:generate go run mksizeclasses.go") 52 fmt.Fprintln(&b) 53 fmt.Fprintln(&b, "package runtime") 54 classes := makeClasses() 55 56 printComment(&b, classes) 57 58 printClasses(&b, classes) 59 60 out, err := format.Source(b.Bytes()) 61 if err != nil { 62 log.Fatal(err) 63 } 64 if *stdout { 65 _, err = os.Stdout.Write(out) 66 } else { 67 err = os.WriteFile("sizeclasses.go", out, 0666) 68 } 69 if err != nil { 70 log.Fatal(err) 71 } 72 } 73 74 const ( 75 // Constants that we use and will transfer to the runtime. 76 maxSmallSize = 32 << 10 77 smallSizeDiv = 8 78 smallSizeMax = 1024 79 largeSizeDiv = 128 80 pageShift = 13 81 82 // Derived constants. 83 pageSize = 1 << pageShift 84 ) 85 86 type class struct { 87 size int // max size 88 npages int // number of pages 89 90 mul int 91 shift uint 92 shift2 uint 93 mask int 94 } 95 96 func powerOfTwo(x int) bool { 97 return x != 0 && x&(x-1) == 0 98 } 99 100 func makeClasses() []class { 101 var classes []class 102 103 classes = append(classes, class{}) // class #0 is a dummy entry 104 105 align := 8 106 for size := align; size <= maxSmallSize; size += align { 107 if powerOfTwo(size) { // bump alignment once in a while 108 if size >= 2048 { 109 align = 256 110 } else if size >= 128 { 111 align = size / 8 112 } else if size >= 32 { 113 align = 16 // heap bitmaps assume 16 byte alignment for allocations >= 32 bytes. 114 } 115 } 116 if !powerOfTwo(align) { 117 panic("incorrect alignment") 118 } 119 120 // Make the allocnpages big enough that 121 // the leftover is less than 1/8 of the total, 122 // so wasted space is at most 12.5%. 123 allocsize := pageSize 124 for allocsize%size > allocsize/8 { 125 allocsize += pageSize 126 } 127 npages := allocsize / pageSize 128 129 // If the previous sizeclass chose the same 130 // allocation size and fit the same number of 131 // objects into the page, we might as well 132 // use just this size instead of having two 133 // different sizes. 134 if len(classes) > 1 && npages == classes[len(classes)-1].npages && allocsize/size == allocsize/classes[len(classes)-1].size { 135 classes[len(classes)-1].size = size 136 continue 137 } 138 classes = append(classes, class{size: size, npages: npages}) 139 } 140 141 // Increase object sizes if we can fit the same number of larger objects 142 // into the same number of pages. For example, we choose size 8448 above 143 // with 6 objects in 7 pages. But we can well use object size 9472, 144 // which is also 6 objects in 7 pages but +1024 bytes (+12.12%). 145 // We need to preserve at least largeSizeDiv alignment otherwise 146 // sizeToClass won't work. 147 for i := range classes { 148 if i == 0 { 149 continue 150 } 151 c := &classes[i] 152 psize := c.npages * pageSize 153 new_size := (psize / (psize / c.size)) &^ (largeSizeDiv - 1) 154 if new_size > c.size { 155 c.size = new_size 156 } 157 } 158 159 if len(classes) != 68 { 160 panic("number of size classes has changed") 161 } 162 163 for i := range classes { 164 computeDivMagic(&classes[i]) 165 } 166 167 return classes 168 } 169 170 // computeDivMagic computes some magic constants to implement 171 // the division required to compute object number from span offset. 172 // n / c.size is implemented as n >> c.shift * c.mul >> c.shift2 173 // for all 0 <= n <= c.npages * pageSize 174 func computeDivMagic(c *class) { 175 // divisor 176 d := c.size 177 if d == 0 { 178 return 179 } 180 181 // maximum input value for which the formula needs to work. 182 max := c.npages * pageSize 183 184 if powerOfTwo(d) { 185 // If the size is a power of two, heapBitsForObject can divide even faster by masking. 186 // Compute this mask. 187 if max >= 1<<16 { 188 panic("max too big for power of two size") 189 } 190 c.mask = 1<<16 - d 191 } 192 193 // Compute pre-shift by factoring power of 2 out of d. 194 for d%2 == 0 { 195 c.shift++ 196 d >>= 1 197 max >>= 1 198 } 199 200 // Find the smallest k that works. 201 // A small k allows us to fit the math required into 32 bits 202 // so we can use 32-bit multiplies and shifts on 32-bit platforms. 203 nextk: 204 for k := uint(0); ; k++ { 205 mul := (int(1)<<k + d - 1) / d // ā2^k / dā 206 207 // Test to see if mul works. 208 for n := 0; n <= max; n++ { 209 if n*mul>>k != n/d { 210 continue nextk 211 } 212 } 213 if mul >= 1<<16 { 214 panic("mul too big") 215 } 216 if uint64(mul)*uint64(max) >= 1<<32 { 217 panic("mul*max too big") 218 } 219 c.mul = mul 220 c.shift2 = k 221 break 222 } 223 224 // double-check. 225 for n := 0; n <= max; n++ { 226 if n*c.mul>>c.shift2 != n/d { 227 fmt.Printf("d=%d max=%d mul=%d shift2=%d n=%d\n", d, max, c.mul, c.shift2, n) 228 panic("bad multiply magic") 229 } 230 // Also check the exact computations that will be done by the runtime, 231 // for both 32 and 64 bit operations. 232 if uint32(n)*uint32(c.mul)>>uint8(c.shift2) != uint32(n/d) { 233 fmt.Printf("d=%d max=%d mul=%d shift2=%d n=%d\n", d, max, c.mul, c.shift2, n) 234 panic("bad 32-bit multiply magic") 235 } 236 if uint64(n)*uint64(c.mul)>>uint8(c.shift2) != uint64(n/d) { 237 fmt.Printf("d=%d max=%d mul=%d shift2=%d n=%d\n", d, max, c.mul, c.shift2, n) 238 panic("bad 64-bit multiply magic") 239 } 240 } 241 } 242 243 func printComment(w io.Writer, classes []class) { 244 fmt.Fprintf(w, "// %-5s %-9s %-10s %-7s %-10s %-9s\n", "class", "bytes/obj", "bytes/span", "objects", "tail waste", "max waste") 245 prevSize := 0 246 for i, c := range classes { 247 if i == 0 { 248 continue 249 } 250 spanSize := c.npages * pageSize 251 objects := spanSize / c.size 252 tailWaste := spanSize - c.size*(spanSize/c.size) 253 maxWaste := float64((c.size-prevSize-1)*objects+tailWaste) / float64(spanSize) 254 prevSize = c.size 255 fmt.Fprintf(w, "// %5d %9d %10d %7d %10d %8.2f%%\n", i, c.size, spanSize, objects, tailWaste, 100*maxWaste) 256 } 257 fmt.Fprintf(w, "\n") 258 } 259 260 func printClasses(w io.Writer, classes []class) { 261 fmt.Fprintln(w, "const (") 262 fmt.Fprintf(w, "_MaxSmallSize = %d\n", maxSmallSize) 263 fmt.Fprintf(w, "smallSizeDiv = %d\n", smallSizeDiv) 264 fmt.Fprintf(w, "smallSizeMax = %d\n", smallSizeMax) 265 fmt.Fprintf(w, "largeSizeDiv = %d\n", largeSizeDiv) 266 fmt.Fprintf(w, "_NumSizeClasses = %d\n", len(classes)) 267 fmt.Fprintf(w, "_PageShift = %d\n", pageShift) 268 fmt.Fprintln(w, ")") 269 270 fmt.Fprint(w, "var class_to_size = [_NumSizeClasses]uint16 {") 271 for _, c := range classes { 272 fmt.Fprintf(w, "%d,", c.size) 273 } 274 fmt.Fprintln(w, "}") 275 276 fmt.Fprint(w, "var class_to_allocnpages = [_NumSizeClasses]uint8 {") 277 for _, c := range classes { 278 fmt.Fprintf(w, "%d,", c.npages) 279 } 280 fmt.Fprintln(w, "}") 281 282 fmt.Fprintln(w, "type divMagic struct {") 283 fmt.Fprintln(w, " shift uint8") 284 fmt.Fprintln(w, " shift2 uint8") 285 fmt.Fprintln(w, " mul uint16") 286 fmt.Fprintln(w, " baseMask uint16") 287 fmt.Fprintln(w, "}") 288 fmt.Fprint(w, "var class_to_divmagic = [_NumSizeClasses]divMagic {") 289 for _, c := range classes { 290 fmt.Fprintf(w, "{%d,%d,%d,%d},", c.shift, c.shift2, c.mul, c.mask) 291 } 292 fmt.Fprintln(w, "}") 293 294 // map from size to size class, for small sizes. 295 sc := make([]int, smallSizeMax/smallSizeDiv+1) 296 for i := range sc { 297 size := i * smallSizeDiv 298 for j, c := range classes { 299 if c.size >= size { 300 sc[i] = j 301 break 302 } 303 } 304 } 305 fmt.Fprint(w, "var size_to_class8 = [smallSizeMax/smallSizeDiv+1]uint8 {") 306 for _, v := range sc { 307 fmt.Fprintf(w, "%d,", v) 308 } 309 fmt.Fprintln(w, "}") 310 311 // map from size to size class, for large sizes. 312 sc = make([]int, (maxSmallSize-smallSizeMax)/largeSizeDiv+1) 313 for i := range sc { 314 size := smallSizeMax + i*largeSizeDiv 315 for j, c := range classes { 316 if c.size >= size { 317 sc[i] = j 318 break 319 } 320 } 321 } 322 fmt.Fprint(w, "var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv+1]uint8 {") 323 for _, v := range sc { 324 fmt.Fprintf(w, "%d,", v) 325 } 326 fmt.Fprintln(w, "}") 327 }