github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/src/runtime/msize.go (about)

     1  // Copyright 2009 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  // Malloc small size classes.
     6  //
     7  // See malloc.h for overview.
     8  //
     9  // The size classes are chosen so that rounding an allocation
    10  // request up to the next size class wastes at most 12.5% (1.125x).
    11  //
    12  // Each size class has its own page count that gets allocated
    13  // and chopped up when new objects of the size class are needed.
    14  // That page count is chosen so that chopping up the run of
    15  // pages into objects of the given size wastes at most 12.5% (1.125x)
    16  // of the memory.  It is not necessary that the cutoff here be
    17  // the same as above.
    18  //
    19  // The two sources of waste multiply, so the worst possible case
    20  // for the above constraints would be that allocations of some
    21  // size might have a 26.6% (1.266x) overhead.
    22  // In practice, only one of the wastes comes into play for a
    23  // given size (sizes < 512 waste mainly on the round-up,
    24  // sizes > 512 waste mainly on the page chopping).
    25  //
    26  // TODO(rsc): Compute max waste for any given size.
    27  
    28  package runtime
    29  
    30  //var class_to_size [_NumSizeClasses]int32
    31  //var class_to_allocnpages [_NumSizeClasses]int32
    32  
    33  // The SizeToClass lookup is implemented using two arrays,
    34  // one mapping sizes <= 1024 to their class and one mapping
    35  // sizes >= 1024 and <= MaxSmallSize to their class.
    36  // All objects are 8-aligned, so the first array is indexed by
    37  // the size divided by 8 (rounded up).  Objects >= 1024 bytes
    38  // are 128-aligned, so the second array is indexed by the
    39  // size divided by 128 (rounded up).  The arrays are filled in
    40  // by InitSizes.
    41  //var size_to_class8 [1024/8 + 1]int8
    42  //var size_to_class128 [(_MaxSmallSize-1024)/128 + 1]int8
    43  
    44  func sizeToClass(size int32) int32 {
    45  	if size > _MaxSmallSize {
    46  		gothrow("SizeToClass - invalid size")
    47  	}
    48  	if size > 1024-8 {
    49  		return int32(size_to_class128[(size-1024+127)>>7])
    50  	}
    51  	return int32(size_to_class8[(size+7)>>3])
    52  }
    53  
    54  func initSizes() {
    55  	// Initialize the runtime·class_to_size table (and choose class sizes in the process).
    56  	class_to_size[0] = 0
    57  	sizeclass := 1 // 0 means no class
    58  	align := 8
    59  	for size := align; size <= _MaxSmallSize; size += align {
    60  		if size&(size-1) == 0 { // bump alignment once in a while
    61  			if size >= 2048 {
    62  				align = 256
    63  			} else if size >= 128 {
    64  				align = size / 8
    65  			} else if size >= 16 {
    66  				align = 16 // required for x86 SSE instructions, if we want to use them
    67  			}
    68  		}
    69  		if align&(align-1) != 0 {
    70  			gothrow("InitSizes - bug")
    71  		}
    72  
    73  		// Make the allocnpages big enough that
    74  		// the leftover is less than 1/8 of the total,
    75  		// so wasted space is at most 12.5%.
    76  		allocsize := _PageSize
    77  		for allocsize%size > allocsize/8 {
    78  			allocsize += _PageSize
    79  		}
    80  		npages := allocsize >> _PageShift
    81  
    82  		// If the previous sizeclass chose the same
    83  		// allocation size and fit the same number of
    84  		// objects into the page, we might as well
    85  		// use just this size instead of having two
    86  		// different sizes.
    87  		if sizeclass > 1 && npages == int(class_to_allocnpages[sizeclass-1]) && allocsize/size == allocsize/int(class_to_size[sizeclass-1]) {
    88  			class_to_size[sizeclass-1] = int32(size)
    89  			continue
    90  		}
    91  
    92  		class_to_allocnpages[sizeclass] = int32(npages)
    93  		class_to_size[sizeclass] = int32(size)
    94  		sizeclass++
    95  	}
    96  	if sizeclass != _NumSizeClasses {
    97  		print("sizeclass=", sizeclass, " NumSizeClasses=", _NumSizeClasses, "\n")
    98  		gothrow("InitSizes - bad NumSizeClasses")
    99  	}
   100  
   101  	// Initialize the size_to_class tables.
   102  	nextsize := 0
   103  	for sizeclass = 1; sizeclass < _NumSizeClasses; sizeclass++ {
   104  		for ; nextsize < 1024 && nextsize <= int(class_to_size[sizeclass]); nextsize += 8 {
   105  			size_to_class8[nextsize/8] = int8(sizeclass)
   106  		}
   107  		if nextsize >= 1024 {
   108  			for ; nextsize <= int(class_to_size[sizeclass]); nextsize += 128 {
   109  				size_to_class128[(nextsize-1024)/128] = int8(sizeclass)
   110  			}
   111  		}
   112  	}
   113  
   114  	// Double-check SizeToClass.
   115  	if false {
   116  		for n := int32(0); n < _MaxSmallSize; n++ {
   117  			sizeclass := sizeToClass(n)
   118  			if sizeclass < 1 || sizeclass >= _NumSizeClasses || class_to_size[sizeclass] < n {
   119  				print("size=", n, " sizeclass=", sizeclass, " runtime·class_to_size=", class_to_size[sizeclass], "\n")
   120  				print("incorrect SizeToClass\n")
   121  				goto dump
   122  			}
   123  			if sizeclass > 1 && class_to_size[sizeclass-1] >= n {
   124  				print("size=", n, " sizeclass=", sizeclass, " runtime·class_to_size=", class_to_size[sizeclass], "\n")
   125  				print("SizeToClass too big\n")
   126  				goto dump
   127  			}
   128  		}
   129  	}
   130  
   131  	testdefersizes()
   132  
   133  	// Copy out for statistics table.
   134  	for i := 0; i < len(class_to_size); i++ {
   135  		memstats.by_size[i].size = uint32(class_to_size[i])
   136  	}
   137  	return
   138  
   139  dump:
   140  	if true {
   141  		print("NumSizeClasses=", _NumSizeClasses, "\n")
   142  		print("runtime·class_to_size:")
   143  		for sizeclass = 0; sizeclass < _NumSizeClasses; sizeclass++ {
   144  			print(" ", class_to_size[sizeclass], "")
   145  		}
   146  		print("\n\n")
   147  		print("size_to_class8:")
   148  		for i := 0; i < len(size_to_class8); i++ {
   149  			print(" ", i*8, "=>", size_to_class8[i], "(", class_to_size[size_to_class8[i]], ")\n")
   150  		}
   151  		print("\n")
   152  		print("size_to_class128:")
   153  		for i := 0; i < len(size_to_class128); i++ {
   154  			print(" ", i*128, "=>", size_to_class128[i], "(", class_to_size[size_to_class128[i]], ")\n")
   155  		}
   156  		print("\n")
   157  	}
   158  	gothrow("InitSizes failed")
   159  }
   160  
   161  // Returns size of the memory block that mallocgc will allocate if you ask for the size.
   162  func roundupsize(size uintptr) uintptr {
   163  	if size < _MaxSmallSize {
   164  		if size <= 1024-8 {
   165  			return uintptr(class_to_size[size_to_class8[(size+7)>>3]])
   166  		} else {
   167  			return uintptr(class_to_size[size_to_class128[(size-1024+127)>>7]])
   168  		}
   169  	}
   170  	if size+_PageSize < size {
   171  		return size
   172  	}
   173  	return round(size, _PageSize)
   174  }