github.com/panjjo/go@v0.0.0-20161104043856-d62b31386338/src/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 // 28 // TODO(rsc): Compute max waste for any given size. 29 30 package main 31 32 import ( 33 "bytes" 34 "flag" 35 "fmt" 36 "go/format" 37 "io" 38 "io/ioutil" 39 "log" 40 "os" 41 ) 42 43 // Generate msize.go 44 45 var stdout = flag.Bool("stdout", false, "write to stdout instead of sizeclasses.go") 46 47 func main() { 48 flag.Parse() 49 50 var b bytes.Buffer 51 fmt.Fprintln(&b, "// AUTO-GENERATED by mksizeclasses.go; DO NOT EDIT") 52 fmt.Fprintln(&b, "//go:generate go run mksizeclasses.go") 53 fmt.Fprintln(&b) 54 fmt.Fprintln(&b, "package runtime") 55 classes := makeClasses() 56 57 printClasses(&b, classes) 58 59 out, err := format.Source(b.Bytes()) 60 if err != nil { 61 log.Fatal(err) 62 } 63 if *stdout { 64 _, err = os.Stdout.Write(out) 65 } else { 66 err = ioutil.WriteFile("sizeclasses.go", out, 0666) 67 } 68 if err != nil { 69 log.Fatal(err) 70 } 71 } 72 73 const ( 74 // Constants that we use and will transfer to the runtime. 75 maxSmallSize = 32 << 10 76 smallSizeDiv = 8 77 smallSizeMax = 1024 78 largeSizeDiv = 128 79 pageShift = 13 80 81 // Derived constants. 82 pageSize = 1 << pageShift 83 ) 84 85 type class struct { 86 size int // max size 87 npages int // number of pages 88 89 mul int 90 shift uint 91 shift2 uint 92 mask int 93 } 94 95 func powerOfTwo(x int) bool { 96 return x != 0 && x&(x-1) == 0 97 } 98 99 func makeClasses() []class { 100 var classes []class 101 102 classes = append(classes, class{}) // class #0 is a dummy entry 103 104 align := 8 105 for size := align; size <= maxSmallSize; size += align { 106 if powerOfTwo(size) { // bump alignment once in a while 107 if size >= 2048 { 108 align = 256 109 } else if size >= 128 { 110 align = size / 8 111 } else if size >= 16 { 112 align = 16 // required for x86 SSE instructions, if we want to use them 113 } 114 } 115 if !powerOfTwo(align) { 116 panic("incorrect alignment") 117 } 118 119 // Make the allocnpages big enough that 120 // the leftover is less than 1/8 of the total, 121 // so wasted space is at most 12.5%. 122 allocsize := pageSize 123 for allocsize%size > allocsize/8 { 124 allocsize += pageSize 125 } 126 npages := allocsize / pageSize 127 128 // If the previous sizeclass chose the same 129 // allocation size and fit the same number of 130 // objects into the page, we might as well 131 // use just this size instead of having two 132 // different sizes. 133 if len(classes) > 1 && npages == classes[len(classes)-1].npages && allocsize/size == allocsize/classes[len(classes)-1].size { 134 classes[len(classes)-1].size = size 135 continue 136 } 137 classes = append(classes, class{size: size, npages: npages}) 138 } 139 140 // Increase object sizes if we can fit the same number of larger objects 141 // into the same number of pages. For example, we choose size 8448 above 142 // with 6 objects in 7 pages. But we can well use object size 9472, 143 // which is also 6 objects in 7 pages but +1024 bytes (+12.12%). 144 // We need to preserve at least largeSizeDiv alignment otherwise 145 // sizeToClass won't work. 146 for i := range classes { 147 if i == 0 { 148 continue 149 } 150 c := &classes[i] 151 psize := c.npages * pageSize 152 new_size := (psize / (psize / c.size)) &^ (largeSizeDiv - 1) 153 if new_size > c.size { 154 c.size = new_size 155 } 156 } 157 158 if len(classes) != 67 { 159 panic("number of size classes has changed") 160 } 161 162 for i := range classes { 163 computeDivMagic(&classes[i]) 164 } 165 166 return classes 167 } 168 169 // computeDivMagic computes some magic constants to implement 170 // the division required to compute object number from span offset. 171 // n / c.size is implemented as n >> c.shift * c.mul >> c.shift2 172 // for all 0 <= n < c.npages * pageSize 173 func computeDivMagic(c *class) { 174 // divisor 175 d := c.size 176 if d == 0 { 177 return 178 } 179 180 // maximum input value for which the formula needs to work. 181 max := c.npages*pageSize - 1 182 183 if powerOfTwo(d) { 184 // If the size is a power of two, heapBitsForObject can divide even faster by masking. 185 // Compute this mask. 186 if max >= 1<<16 { 187 panic("max too big for power of two size") 188 } 189 c.mask = 1<<16 - d 190 } 191 192 // Compute pre-shift by factoring power of 2 out of d. 193 for d%2 == 0 { 194 c.shift++ 195 d >>= 1 196 max >>= 1 197 } 198 199 // Find the smallest k that works. 200 // A small k allows us to fit the math required into 32 bits 201 // so we can use 32-bit multiplies and shifts on 32-bit platforms. 202 nextk: 203 for k := uint(0); ; k++ { 204 mul := (int(1)<<k + d - 1) / d // ā2^k / dā 205 206 // Test to see if mul works. 207 for n := 0; n <= max; n++ { 208 if n*mul>>k != n/d { 209 continue nextk 210 } 211 } 212 if mul >= 1<<16 { 213 panic("mul too big") 214 } 215 if uint64(mul)*uint64(max) >= 1<<32 { 216 panic("mul*max too big") 217 } 218 c.mul = mul 219 c.shift2 = k 220 break 221 } 222 223 // double-check. 224 for n := 0; n <= max; n++ { 225 if n*c.mul>>c.shift2 != n/d { 226 fmt.Printf("d=%d max=%d mul=%d shift2=%d n=%d\n", d, max, c.mul, c.shift2, n) 227 panic("bad multiply magic") 228 } 229 // Also check the exact computations that will be done by the runtime, 230 // for both 32 and 64 bit operations. 231 if uint32(n)*uint32(c.mul)>>uint8(c.shift2) != uint32(n/d) { 232 fmt.Printf("d=%d max=%d mul=%d shift2=%d n=%d\n", d, max, c.mul, c.shift2, n) 233 panic("bad 32-bit multiply magic") 234 } 235 if uint64(n)*uint64(c.mul)>>uint8(c.shift2) != uint64(n/d) { 236 fmt.Printf("d=%d max=%d mul=%d shift2=%d n=%d\n", d, max, c.mul, c.shift2, n) 237 panic("bad 64-bit multiply magic") 238 } 239 } 240 } 241 242 func printClasses(w io.Writer, classes []class) { 243 fmt.Fprintln(w, "const (") 244 fmt.Fprintf(w, "_MaxSmallSize = %d\n", maxSmallSize) 245 fmt.Fprintf(w, "smallSizeDiv = %d\n", smallSizeDiv) 246 fmt.Fprintf(w, "smallSizeMax = %d\n", smallSizeMax) 247 fmt.Fprintf(w, "largeSizeDiv = %d\n", largeSizeDiv) 248 fmt.Fprintf(w, "_NumSizeClasses = %d\n", len(classes)) 249 fmt.Fprintf(w, "_PageShift = %d\n", pageShift) 250 fmt.Fprintln(w, ")") 251 252 fmt.Fprint(w, "var class_to_size = [_NumSizeClasses]uint16 {") 253 for _, c := range classes { 254 fmt.Fprintf(w, "%d,", c.size) 255 } 256 fmt.Fprintln(w, "}") 257 258 fmt.Fprint(w, "var class_to_allocnpages = [_NumSizeClasses]uint8 {") 259 for _, c := range classes { 260 fmt.Fprintf(w, "%d,", c.npages) 261 } 262 fmt.Fprintln(w, "}") 263 264 fmt.Fprintln(w, "type divMagic struct {") 265 fmt.Fprintln(w, " shift uint8") 266 fmt.Fprintln(w, " shift2 uint8") 267 fmt.Fprintln(w, " mul uint16") 268 fmt.Fprintln(w, " baseMask uint16") 269 fmt.Fprintln(w, "}") 270 fmt.Fprint(w, "var class_to_divmagic = [_NumSizeClasses]divMagic {") 271 for _, c := range classes { 272 fmt.Fprintf(w, "{%d,%d,%d,%d},", c.shift, c.shift2, c.mul, c.mask) 273 } 274 fmt.Fprintln(w, "}") 275 276 // map from size to size class, for small sizes. 277 sc := make([]int, smallSizeMax/smallSizeDiv+1) 278 for i := range sc { 279 size := i * smallSizeDiv 280 for j, c := range classes { 281 if c.size >= size { 282 sc[i] = j 283 break 284 } 285 } 286 } 287 fmt.Fprint(w, "var size_to_class8 = [smallSizeMax/smallSizeDiv+1]uint8 {") 288 for _, v := range sc { 289 fmt.Fprintf(w, "%d,", v) 290 } 291 fmt.Fprintln(w, "}") 292 293 // map from size to size class, for large sizes. 294 sc = make([]int, (maxSmallSize-smallSizeMax)/largeSizeDiv+1) 295 for i := range sc { 296 size := smallSizeMax + i*largeSizeDiv 297 for j, c := range classes { 298 if c.size >= size { 299 sc[i] = j 300 break 301 } 302 } 303 } 304 fmt.Fprint(w, "var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv+1]uint8 {") 305 for _, v := range sc { 306 fmt.Fprintf(w, "%d,", v) 307 } 308 fmt.Fprintln(w, "}") 309 }