// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Malloc small size classes. // // See malloc.h for overview. // // The size classes are chosen so that rounding an allocation // request up to the next size class wastes at most 12.5% (1.125x). // // Each size class has its own page count that gets allocated // and chopped up when new objects of the size class are needed. // That page count is chosen so that chopping up the run of // pages into objects of the given size wastes at most 12.5% (1.125x) // of the memory. It is not necessary that the cutoff here be // the same as above. // // The two sources of waste multiply, so the worst possible case // for the above constraints would be that allocations of some // size might have a 26.6% (1.266x) overhead. // In practice, only one of the wastes comes into play for a // given size (sizes < 512 waste mainly on the round-up, // sizes > 512 waste mainly on the page chopping). // // TODO(rsc): Compute max waste for any given size. #include "runtime.h" #include "arch_GOARCH.h" #include "malloc.h" #include "../../cmd/ld/textflag.h" #pragma dataflag NOPTR int32 runtime·class_to_size[NumSizeClasses]; #pragma dataflag NOPTR int32 runtime·class_to_allocnpages[NumSizeClasses]; // The SizeToClass lookup is implemented using two arrays, // one mapping sizes <= 1024 to their class and one mapping // sizes >= 1024 and <= MaxSmallSize to their class. // All objects are 8-aligned, so the first array is indexed by // the size divided by 8 (rounded up). Objects >= 1024 bytes // are 128-aligned, so the second array is indexed by the // size divided by 128 (rounded up). The arrays are filled in // by InitSizes. #pragma dataflag NOPTR int8 runtime·size_to_class8[1024/8 + 1]; #pragma dataflag NOPTR int8 runtime·size_to_class128[(MaxSmallSize-1024)/128 + 1]; void runtime·testdefersizes(void); int32 runtime·SizeToClass(int32 size) { if(size > MaxSmallSize) runtime·throw("SizeToClass - invalid size"); if(size > 1024-8) return runtime·size_to_class128[(size-1024+127) >> 7]; return runtime·size_to_class8[(size+7)>>3]; } void runtime·InitSizes(void) { int32 align, sizeclass, size, nextsize, n; uint32 i; uintptr allocsize, npages; // Initialize the runtime·class_to_size table (and choose class sizes in the process). runtime·class_to_size[0] = 0; sizeclass = 1; // 0 means no class align = 8; for(size = align; size <= MaxSmallSize; size += align) { if((size&(size-1)) == 0) { // bump alignment once in a while if(size >= 2048) align = 256; else if(size >= 128) align = size / 8; else if(size >= 16) align = 16; // required for x86 SSE instructions, if we want to use them } if((align&(align-1)) != 0) runtime·throw("InitSizes - bug"); // Make the allocnpages big enough that // the leftover is less than 1/8 of the total, // so wasted space is at most 12.5%. allocsize = PageSize; while(allocsize%size > allocsize/8) allocsize += PageSize; npages = allocsize >> PageShift; // If the previous sizeclass chose the same // allocation size and fit the same number of // objects into the page, we might as well // use just this size instead of having two // different sizes. if(sizeclass > 1 && npages == runtime·class_to_allocnpages[sizeclass-1] && allocsize/size == allocsize/runtime·class_to_size[sizeclass-1]) { runtime·class_to_size[sizeclass-1] = size; continue; } runtime·class_to_allocnpages[sizeclass] = npages; runtime·class_to_size[sizeclass] = size; sizeclass++; } if(sizeclass != NumSizeClasses) { runtime·printf("sizeclass=%d NumSizeClasses=%d\n", sizeclass, NumSizeClasses); runtime·throw("InitSizes - bad NumSizeClasses"); } // Initialize the size_to_class tables. nextsize = 0; for (sizeclass = 1; sizeclass < NumSizeClasses; sizeclass++) { for(; nextsize < 1024 && nextsize <= runtime·class_to_size[sizeclass]; nextsize+=8) runtime·size_to_class8[nextsize/8] = sizeclass; if(nextsize >= 1024) for(; nextsize <= runtime·class_to_size[sizeclass]; nextsize += 128) runtime·size_to_class128[(nextsize-1024)/128] = sizeclass; } // Double-check SizeToClass. if(0) { for(n=0; n < MaxSmallSize; n++) { sizeclass = runtime·SizeToClass(n); if(sizeclass < 1 || sizeclass >= NumSizeClasses || runtime·class_to_size[sizeclass] < n) { runtime·printf("size=%d sizeclass=%d runtime·class_to_size=%d\n", n, sizeclass, runtime·class_to_size[sizeclass]); runtime·printf("incorrect SizeToClass"); goto dump; } if(sizeclass > 1 && runtime·class_to_size[sizeclass-1] >= n) { runtime·printf("size=%d sizeclass=%d runtime·class_to_size=%d\n", n, sizeclass, runtime·class_to_size[sizeclass]); runtime·printf("SizeToClass too big"); goto dump; } } } runtime·testdefersizes(); // Copy out for statistics table. for(i=0; i%d(%d)\n", i*8, runtime·size_to_class8[i], runtime·class_to_size[runtime·size_to_class8[i]]); runtime·printf("\n"); runtime·printf("size_to_class128:"); for(i=0; i%d(%d)\n", i*128, runtime·size_to_class128[i], runtime·class_to_size[runtime·size_to_class128[i]]); runtime·printf("\n"); } runtime·throw("InitSizes failed"); } // Returns size of the memory block that mallocgc will allocate if you ask for the size. uintptr runtime·roundupsize(uintptr size) { if(size < MaxSmallSize) { if(size <= 1024-8) return runtime·class_to_size[runtime·size_to_class8[(size+7)>>3]]; else return runtime·class_to_size[runtime·size_to_class128[(size-1024+127) >> 7]]; } if(size + PageSize < size) return size; return ROUND(size, PageSize); }