1 module d.gc.sizeclass;
2 
3 import d.gc.spec;
4 
5 enum ClassCount {
6 	Tiny = getTinyClassCount(),
7 	Small = getSmallClassCount(),
8 	Large = getLargeClassCount(),
9 	Total = getTotalClassCount(),
10 	Lookup = getLookupClassCount(),
11 }
12 
13 enum SizeClass {
14 	Small = getSizeFromBinID(ClassCount.Small - 1),
15 	Large = getSizeFromBinID(ClassCount.Large - 1),
16 }
17 
18 size_t getAllocSize(size_t size) {
19 	if (LgTiny < LgQuantum && size < (1UL << LgQuantum)) {
20 		// Not the fastest way to handle this.
21 		import d.gc.util;
22 		auto s = pow2ceil(size);
23 
24 		enum T = 1UL << LgTiny;
25 		return (s < T) ? T : s;
26 	}
27 
28 	import d.gc.util;
29 	auto shift =
30 		(size < (1UL << LgQuantum + 2)) ? LgQuantum : lg2floor(size - 1) - 2;
31 
32 	return (((size - 1) >> shift) + 1) << shift;
33 }
34 
35 ubyte getBinID(size_t size) {
36 	if (LgTiny < LgQuantum && size < (1UL << LgQuantum)) {
37 		// Not the fastest way to handle this.
38 		import d.gc.util;
39 		auto ret = lg2floor(pow2ceil(size) >> LgTiny);
40 
41 		// TODO: out contract.
42 		assert(ret < ubyte.max);
43 		return cast(ubyte) ret;
44 	}
45 
46 	// Faster way to compute x = lg2floor(pow2ceil(size));
47 	import d.gc.util;
48 	auto shift =
49 		(size < (1UL << (LgQuantum + 2))) ? LgQuantum : lg2floor(size - 1) - 2;
50 
51 	auto mod = (size - 1) >> shift;
52 	auto ret = (shift - LgQuantum) * 4 + mod + ClassCount.Tiny;
53 
54 	// TODO: out contract.
55 	assert(ret < ubyte.max);
56 	return cast(ubyte) ret;
57 }
58 
59 size_t getSizeFromBinID(uint binID) {
60 	if (binID < ClassCount.Small) {
61 		import d.gc.bin;
62 		auto ret = binInfos[binID].itemSize;
63 
64 		// XXX: out contract
65 		assert(binID == getBinID(ret));
66 		assert(ret == getAllocSize(ret));
67 		return ret;
68 	}
69 
70 	auto largeBinID = binID - ClassCount.Small;
71 	auto shift = largeBinID / 4 + LgPageSize;
72 	size_t bits = (largeBinID % 4) | 0x04;
73 
74 	auto ret = bits << shift;
75 
76 	// XXX: out contract
77 	assert(binID == getBinID(ret));
78 	assert(ret == getAllocSize(ret));
79 	return ret;
80 }
81 
82 auto getBinInfos() {
83 	import d.gc.bin;
84 	BinInfo[ClassCount.Small] bins;
85 
86 	void delegate(uint id, uint grp, uint delta, uint ndelta) dg = void;
87 	auto dgSt = cast(BinInfoComputerDg*) &dg;
88 
89 	dgSt.fun = binInfoComputer;
90 	dgSt.bins = &bins;
91 
92 	computeSizeClass(dg);
93 
94 	return bins;
95 }
96 
97 private:
98 
99 enum LgSizeClass {
100 	LgSmall = LgPageSize + 2,
101 	LgLarge = LgSmall + 7,
102 }
103 
104 // XXX: find a better way to do all this.
105 // This is kind of convoluted as I want to avoid alloc.
106 struct BinInfoComputerDg {
107 	void* bins;
108 	void* fun;
109 }
110 
111 void binInfoComputer(void* binsPtr, uint id, uint grp, uint delta,
112                      uint ndelta) {
113 	import d.gc.bin;
114 	auto bins = cast(BinInfo*) binsPtr;
115 
116 	// XXX: 1UL is useless here, but there is a bug in type
117 	// promotion for >= so we need it.
118 	auto s = (1UL << grp) + (ndelta << delta);
119 	if (s >= (1UL << LgSizeClass.LgSmall)) {
120 		return;
121 	}
122 
123 	assert(s < ushort.max);
124 	auto itemSize = cast(ushort) s;
125 
126 	ubyte[4] npLookup;
127 
128 	// XXX: use array initializer.
129 	npLookup[0] = cast(ubyte) (((s - 1) >> LgPageSize) + 1);
130 	npLookup[1] = 5;
131 	npLookup[2] = 3;
132 	npLookup[3] = 7;
133 
134 	auto shift = cast(ubyte) delta;
135 	if (grp == delta) {
136 		auto tag = (ndelta + 1) / 2;
137 		shift = cast(ubyte) (delta + tag - 2);
138 	}
139 
140 	auto needPages = npLookup[(itemSize >> shift) % 4];
141 
142 	uint p = needPages;
143 	auto slots = cast(ushort) ((p << LgPageSize) / s);
144 
145 	assert(id < ClassCount.Small);
146 	bins[id] = BinInfo(itemSize, shift, needPages, slots);
147 }
148 
149 // 64 bits tiny, 128 bits quantum.
150 enum LgTiny = 3;
151 enum LgQuantum = 4;
152 
153 auto getTotalClassCount() {
154 	uint count = 0;
155 
156 	computeSizeClass((uint id, uint grp, uint delta, uint ndelta) {
157 		count++;
158 	});
159 
160 	return count;
161 }
162 
163 auto getTinyClassCount() {
164 	uint count = 0;
165 
166 	computeSizeClass((uint id, uint grp, uint delta, uint ndelta) {
167 		if (grp < LgQuantum) {
168 			count++;
169 		}
170 	});
171 
172 	return count;
173 }
174 
175 auto getSmallClassCount() {
176 	uint count = 0;
177 
178 	computeSizeClass((uint id, uint grp, uint delta, uint ndelta) {
179 		if (grp < LgSizeClass.LgSmall) {
180 			count++;
181 		}
182 	});
183 
184 	return count;
185 }
186 
187 auto getLargeClassCount() {
188 	uint count = 0;
189 
190 	computeSizeClass((uint id, uint grp, uint delta, uint ndelta) {
191 		if (grp < LgSizeClass.LgLarge) {
192 			count++;
193 		}
194 	});
195 
196 	return count + 1;
197 }
198 
199 auto getLookupClassCount() {
200 	uint count = 0;
201 
202 	computeSizeClass((uint id, uint grp, uint delta, uint ndelta) {
203 		if (grp < LgPageSize) {
204 			count++;
205 		}
206 	});
207 
208 	return count + 1;
209 }
210 
211 void computeSizeClass(
212 	void delegate(uint id, uint grp, uint delta, uint ndelta) fun
213 ) {
214 	uint id = 0;
215 
216 	// Tiny sizes.
217 	foreach (grp; LgTiny .. LgQuantum) {
218 		fun(id++, grp, grp, 0);
219 	}
220 
221 	// First group is kind of special.
222 	foreach (i; 0 .. 3) {
223 		fun(id++, LgQuantum, LgQuantum, i);
224 	}
225 
226 	// Most size classes falls here.
227 	foreach (grp; LgQuantum + 2 .. SizeofPtr * 8) {
228 		foreach (i; 0 .. 4) {
229 			fun(id++, grp, grp - 2, i);
230 		}
231 	}
232 
233 	// We want to be able to store the binID in a byte.
234 	assert(id <= ubyte.max);
235 }
236 
237 void printfAlloc(size_t s) {
238 	import d.gc.util, core.stdc.stdio;
239 	printf("%lu :\t%lu\t%hhu\n".ptr, s, getAllocSize(s), getBinID(s));
240 }
241 
242 void main() {
243 	computeSizeClass((uint id, uint grp, uint delta, uint ndelta) {
244 		import core.stdc.stdio;
245 		printf("%d\t%d\t%d\t%d\t0x%lx\n".ptr, id, grp, delta, ndelta,
246 		       (1UL << grp) + ndelta * (1UL << delta));
247 	});
248 
249 	import core.stdc.stdio;
250 	printf("total: %d\tsmall: %d\tlarge: %d\tlookup: %d\n".ptr,
251 	       ClassCount.Total, ClassCount.Small, ClassCount.Large,
252 	       ClassCount.Lookup);
253 
254 	auto bins = getBinInfos();
255 
256 	printf("bins:\n".ptr);
257 	foreach (i; 0 .. ClassCount.Small) {
258 		auto b = bins[i];
259 		printf("id: %d\tsize: %hd\tneedPages: %hhd\tslots: %hd\n".ptr, i,
260 		       b.itemSize, b.needPages, b.slots);
261 	}
262 
263 	printf("allocs:\n".ptr);
264 	printfAlloc(0);
265 	printfAlloc(5);
266 	printfAlloc(8);
267 	printfAlloc(9);
268 	printfAlloc(16);
269 	printfAlloc(17);
270 	printfAlloc(32);
271 	printfAlloc(33);
272 	printfAlloc(48);
273 	printfAlloc(49);
274 	printfAlloc(64);
275 	printfAlloc(65);
276 	printfAlloc(80);
277 	printfAlloc(81);
278 	printfAlloc(96);
279 	printfAlloc(97);
280 	printfAlloc(112);
281 	printfAlloc(113);
282 	printfAlloc(128);
283 	printfAlloc(129);
284 	printfAlloc(160);
285 	printfAlloc(161);
286 	printfAlloc(192);
287 
288 	printfAlloc(1UL << 63);
289 	printfAlloc((1UL << 63) + 1);
290 	printfAlloc((1UL << 63) + (1UL << 61));
291 	printfAlloc((1UL << 63) + (1UL << 61) + 1);
292 	printfAlloc((1UL << 63) + (2UL << 61));
293 	printfAlloc((1UL << 63) + (2UL << 61) + 1);
294 	printfAlloc((1UL << 63) + (3UL << 61));
295 	printfAlloc((1UL << 63) + (3UL << 61) + 1);
296 }