1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.buffer.api.pool;
17
18 import java.util.concurrent.ConcurrentHashMap;
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 abstract class SizeClasses implements SizeClassesMetric {
82 private static final ConcurrentHashMap<SizeClassKey, SizeClassValue> CACHE =
83 new ConcurrentHashMap<>();
84
85 static final int LOG2_QUANTUM = 4;
86
87 private static final int LOG2_SIZE_CLASS_GROUP = 2;
88 private static final int LOG2_MAX_LOOKUP_SIZE = 12;
89
90 private static final int LOG2GROUP_IDX = 1;
91 private static final int LOG2DELTA_IDX = 2;
92 private static final int NDELTA_IDX = 3;
93 private static final int PAGESIZE_IDX = 4;
94 private static final int SUBPAGE_IDX = 5;
95 private static final int LOG2_DELTA_LOOKUP_IDX = 6;
96
97 private static final byte no = 0, yes = 1;
98
99 protected SizeClasses(int pageSize, int pageShifts, int chunkSize, int directMemoryCacheAlignment) {
100 this.pageSize = pageSize;
101 this.pageShifts = pageShifts;
102 this.chunkSize = chunkSize;
103 this.directMemoryCacheAlignment = directMemoryCacheAlignment;
104
105 SizeClassValue value = CACHE.computeIfAbsent(
106 new SizeClassKey(pageSize, pageShifts, chunkSize, directMemoryCacheAlignment),
107 key -> new SizeClassValue(key, directMemoryCacheAlignment));
108 nSizes = value.nSizes;
109 nSubpages = value.nSubpages;
110 nPSizes = value.nPSizes;
111 smallMaxSizeIdx = value.smallMaxSizeIdx;
112 lookupMaxSize = value.lookupMaxSize;
113 pageIdx2sizeTab = value.pageIdx2sizeTab;
114 sizeIdx2sizeTab = value.sizeIdx2sizeTab;
115 size2idxTab = value.size2idxTab;
116 }
117
118 protected final int pageSize;
119 protected final int pageShifts;
120 protected final int chunkSize;
121 protected final int directMemoryCacheAlignment;
122
123 final int nSizes;
124 final int nSubpages;
125 final int nPSizes;
126 final int smallMaxSizeIdx;
127
128 private final int lookupMaxSize;
129 private final int[] pageIdx2sizeTab;
130
131
132 private final int[] sizeIdx2sizeTab;
133
134
135
136 private final int[] size2idxTab;
137
138 @Override
139 public int sizeIdx2size(int sizeIdx) {
140 return sizeIdx2sizeTab[sizeIdx];
141 }
142
143 @Override
144 public int sizeIdx2sizeCompute(int sizeIdx) {
145 int group = sizeIdx >> LOG2_SIZE_CLASS_GROUP;
146 int mod = sizeIdx & (1 << LOG2_SIZE_CLASS_GROUP) - 1;
147
148 int groupSize = group == 0? 0 :
149 1 << LOG2_QUANTUM + LOG2_SIZE_CLASS_GROUP - 1 << group;
150
151 int shift = group == 0? 1 : group;
152 int lgDelta = shift + LOG2_QUANTUM - 1;
153 int modSize = mod + 1 << lgDelta;
154
155 return groupSize + modSize;
156 }
157
158 @Override
159 public long pageIdx2size(int pageIdx) {
160 return pageIdx2sizeTab[pageIdx];
161 }
162
163 @Override
164 public long pageIdx2sizeCompute(int pageIdx) {
165 int group = pageIdx >> LOG2_SIZE_CLASS_GROUP;
166 int mod = pageIdx & (1 << LOG2_SIZE_CLASS_GROUP) - 1;
167
168 long groupSize = group == 0? 0 :
169 1L << pageShifts + LOG2_SIZE_CLASS_GROUP - 1 << group;
170
171 int shift = group == 0? 1 : group;
172 int log2Delta = shift + pageShifts - 1;
173 int modSize = mod + 1 << log2Delta;
174
175 return groupSize + modSize;
176 }
177
178 @Override
179 public int size2SizeIdx(int size) {
180 if (size == 0) {
181 return 0;
182 }
183 if (size > chunkSize) {
184 return nSizes;
185 }
186
187 size = alignSizeIfNeeded(size, directMemoryCacheAlignment);
188
189 if (size <= lookupMaxSize) {
190
191 return size2idxTab[size - 1 >> LOG2_QUANTUM];
192 }
193
194 int x = PoolThreadCache.log2((size << 1) - 1);
195 int shift = x < LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM + 1
196 ? 0 : x - (LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM);
197
198 int group = shift << LOG2_SIZE_CLASS_GROUP;
199
200 int log2Delta = x < LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM + 1
201 ? LOG2_QUANTUM : x - LOG2_SIZE_CLASS_GROUP - 1;
202
203 int deltaInverseMask = -1 << log2Delta;
204 int mod = (size - 1 & deltaInverseMask) >> log2Delta &
205 (1 << LOG2_SIZE_CLASS_GROUP) - 1;
206
207 return group + mod;
208 }
209
210 @Override
211 public int pages2pageIdx(int pages) {
212 return pages2pageIdxCompute(pages, false);
213 }
214
215 @Override
216 public int pages2pageIdxFloor(int pages) {
217 return pages2pageIdxCompute(pages, true);
218 }
219
220 private int pages2pageIdxCompute(int pages, boolean floor) {
221 int pageSize = pages << pageShifts;
222 if (pageSize > chunkSize) {
223 return nPSizes;
224 }
225
226 int x = PoolThreadCache.log2((pageSize << 1) - 1);
227
228 int shift = x < LOG2_SIZE_CLASS_GROUP + pageShifts
229 ? 0 : x - (LOG2_SIZE_CLASS_GROUP + pageShifts);
230
231 int group = shift << LOG2_SIZE_CLASS_GROUP;
232
233 int log2Delta = x < LOG2_SIZE_CLASS_GROUP + pageShifts + 1?
234 pageShifts : x - LOG2_SIZE_CLASS_GROUP - 1;
235
236 int deltaInverseMask = -1 << log2Delta;
237 int mod = (pageSize - 1 & deltaInverseMask) >> log2Delta &
238 (1 << LOG2_SIZE_CLASS_GROUP) - 1;
239
240 int pageIdx = group + mod;
241
242 if (floor && pageIdx2sizeTab[pageIdx] > pages << pageShifts) {
243 pageIdx--;
244 }
245
246 return pageIdx;
247 }
248
249
250 private static int alignSizeIfNeeded(int size, int alignment) {
251 if (alignment <= 0) {
252 return size;
253 }
254 int delta = size & alignment - 1;
255 return delta == 0? size : size + alignment - delta;
256 }
257
258 @Override
259 public int normalizeSize(int size) {
260 if (size == 0) {
261 return sizeIdx2sizeTab[0];
262 }
263 size = alignSizeIfNeeded(size, directMemoryCacheAlignment);
264
265 if (size <= lookupMaxSize) {
266 int ret = sizeIdx2sizeTab[size2idxTab[size - 1 >> LOG2_QUANTUM]];
267 assert ret == normalizeSizeCompute(size);
268 return ret;
269 }
270 return normalizeSizeCompute(size);
271 }
272
273 private static int normalizeSizeCompute(int size) {
274 int x = PoolThreadCache.log2((size << 1) - 1);
275 int log2Delta = x < LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM + 1
276 ? LOG2_QUANTUM : x - LOG2_SIZE_CLASS_GROUP - 1;
277 int delta = 1 << log2Delta;
278 int delta_mask = delta - 1;
279 return size + delta_mask & ~delta_mask;
280 }
281
282 private static final class SizeClassKey {
283 final int pageSize;
284 final int pageShifts;
285 final int chunkSize;
286 final int directMemoryCacheAlignment;
287
288 private SizeClassKey(int pageSize, int pageShifts, int chunkSize, int directMemoryCacheAlignment) {
289 this.pageSize = pageSize;
290 this.pageShifts = pageShifts;
291 this.chunkSize = chunkSize;
292 this.directMemoryCacheAlignment = directMemoryCacheAlignment;
293 }
294
295 @Override
296 public boolean equals(Object o) {
297 if (this == o) {
298 return true;
299 }
300 if (o == null || getClass() != o.getClass()) {
301 return false;
302 }
303
304 SizeClassKey that = (SizeClassKey) o;
305
306 if (pageSize != that.pageSize) {
307 return false;
308 }
309 if (pageShifts != that.pageShifts) {
310 return false;
311 }
312 if (chunkSize != that.chunkSize) {
313 return false;
314 }
315 return directMemoryCacheAlignment == that.directMemoryCacheAlignment;
316 }
317
318 @Override
319 public int hashCode() {
320 int result = pageSize;
321 result = 31 * result + pageShifts;
322 result = 31 * result + chunkSize;
323 result = 31 * result + directMemoryCacheAlignment;
324 return result;
325 }
326 }
327
328 private static final class SizeClassValue {
329 final SizeClassKey key;
330 final int nSizes;
331 final int nSubpages;
332 final int nPSizes;
333 final int smallMaxSizeIdx;
334 final int lookupMaxSize;
335 final int[] pageIdx2sizeTab;
336 final int[] sizeIdx2sizeTab;
337 final int[] size2idxTab;
338
339 SizeClassValue(SizeClassKey key, int directMemoryCacheAlignment) {
340 this.key = key;
341 int group = PoolThreadCache.log2(key.chunkSize) + 1 - LOG2_QUANTUM;
342
343
344
345 short[][] sizeClasses = new short[group << LOG2_SIZE_CLASS_GROUP][7];
346
347 int normalMaxSize = -1;
348 int nSizes = 0;
349 int size = 0;
350
351 int log2Group = LOG2_QUANTUM;
352 int log2Delta = LOG2_QUANTUM;
353 int ndeltaLimit = 1 << LOG2_SIZE_CLASS_GROUP;
354
355
356
357 for (int nDelta = 0; nDelta < ndeltaLimit; nDelta++, nSizes++) {
358 short[] sizeClass = newSizeClass(nSizes, log2Group, log2Delta, nDelta, key);
359 sizeClasses[nSizes] = sizeClass;
360 size = sizeOf(sizeClass, directMemoryCacheAlignment);
361 }
362
363 log2Group += LOG2_SIZE_CLASS_GROUP;
364
365
366 for (; size < key.chunkSize; log2Group++, log2Delta++) {
367 for (int nDelta = 1; nDelta <= ndeltaLimit && size < key.chunkSize; nDelta++, nSizes++) {
368 short[] sizeClass = newSizeClass(nSizes, log2Group, log2Delta, nDelta, key);
369 sizeClasses[nSizes] = sizeClass;
370 size = normalMaxSize = sizeOf(sizeClass, directMemoryCacheAlignment);
371 }
372 }
373
374
375 assert key.chunkSize == normalMaxSize;
376
377 int smallMaxSizeIdx = 0;
378 int lookupMaxSize = 0;
379 int nPSizes = 0;
380 int nSubpages = 0;
381 for (int idx = 0; idx < nSizes; idx++) {
382 short[] sz = sizeClasses[idx];
383 if (sz[PAGESIZE_IDX] == yes) {
384 nPSizes++;
385 }
386 if (sz[SUBPAGE_IDX] == yes) {
387 nSubpages++;
388 smallMaxSizeIdx = idx;
389 }
390 if (sz[LOG2_DELTA_LOOKUP_IDX] != no) {
391 lookupMaxSize = sizeOf(sz, directMemoryCacheAlignment);
392 }
393 }
394 this.smallMaxSizeIdx = smallMaxSizeIdx;
395 this.lookupMaxSize = lookupMaxSize;
396 this.nPSizes = nPSizes;
397 this.nSubpages = nSubpages;
398 this.nSizes = nSizes;
399
400
401 sizeIdx2sizeTab = newIdx2SizeTab(sizeClasses, nSizes, directMemoryCacheAlignment);
402 pageIdx2sizeTab = newPageIdx2sizeTab(sizeClasses, nSizes, nPSizes, directMemoryCacheAlignment);
403 size2idxTab = newSize2idxTab(lookupMaxSize, sizeClasses);
404 }
405
406
407 private static short[] newSizeClass(int index, int log2Group, int log2Delta, int nDelta, SizeClassKey key) {
408 short isMultiPageSize;
409 if (log2Delta >= key.pageShifts) {
410 isMultiPageSize = yes;
411 } else {
412 int pageSize = 1 << key.pageShifts;
413 int size = (1 << log2Group) + (1 << log2Delta) * nDelta;
414
415 isMultiPageSize = size == size / pageSize * pageSize? yes : no;
416 }
417
418 int log2Ndelta = nDelta == 0? 0 : PoolThreadCache.log2(nDelta);
419
420 byte remove = 1 << log2Ndelta < nDelta? yes : no;
421
422 int log2Size = log2Delta + log2Ndelta == log2Group? log2Group + 1 : log2Group;
423 if (log2Size == log2Group) {
424 remove = yes;
425 }
426
427 short isSubpage = log2Size < key.pageShifts + LOG2_SIZE_CLASS_GROUP? yes : no;
428
429 int log2DeltaLookup = log2Size < LOG2_MAX_LOOKUP_SIZE ||
430 log2Size == LOG2_MAX_LOOKUP_SIZE && remove == no
431 ? log2Delta : no;
432
433 return new short[] {
434 (short) index, (short) log2Group, (short) log2Delta,
435 (short) nDelta, isMultiPageSize, isSubpage, (short) log2DeltaLookup
436 };
437 }
438
439 private static int[] newIdx2SizeTab(short[][] sizeClasses, int nSizes, int directMemoryCacheAlignment) {
440 int[] sizeIdx2sizeTab = new int[nSizes];
441
442 for (int i = 0; i < nSizes; i++) {
443 short[] sizeClass = sizeClasses[i];
444 sizeIdx2sizeTab[i] = sizeOf(sizeClass, directMemoryCacheAlignment);
445 }
446 return sizeIdx2sizeTab;
447 }
448
449 private static int calculateSize(int log2Group, int nDelta, int log2Delta) {
450 return (1 << log2Group) + (nDelta << log2Delta);
451 }
452
453 private static int sizeOf(short[] sizeClass, int directMemoryCacheAlignment) {
454 int log2Group = sizeClass[LOG2GROUP_IDX];
455 int log2Delta = sizeClass[LOG2DELTA_IDX];
456 int nDelta = sizeClass[NDELTA_IDX];
457
458 int size = calculateSize(log2Group, nDelta, log2Delta);
459
460 return alignSizeIfNeeded(size, directMemoryCacheAlignment);
461 }
462
463 private static int[] newPageIdx2sizeTab(short[][] sizeClasses, int nSizes, int nPSizes,
464 int directMemoryCacheAlignment) {
465 int[] pageIdx2sizeTab = new int[nPSizes];
466 int pageIdx = 0;
467 for (int i = 0; i < nSizes; i++) {
468 short[] sizeClass = sizeClasses[i];
469 if (sizeClass[PAGESIZE_IDX] == yes) {
470 pageIdx2sizeTab[pageIdx++] = sizeOf(sizeClass, directMemoryCacheAlignment);
471 }
472 }
473 return pageIdx2sizeTab;
474 }
475
476 private static int[] newSize2idxTab(int lookupMaxSize, short[][] sizeClasses) {
477 int[] size2idxTab = new int[lookupMaxSize >> LOG2_QUANTUM];
478 int idx = 0;
479 int size = 0;
480
481 for (int i = 0; size <= lookupMaxSize; i++) {
482 int log2Delta = sizeClasses[i][LOG2DELTA_IDX];
483 int times = 1 << log2Delta - LOG2_QUANTUM;
484
485 while (size <= lookupMaxSize && times-- > 0) {
486 size2idxTab[idx++] = i;
487 size = idx + 1 << LOG2_QUANTUM;
488 }
489 }
490 return size2idxTab;
491 }
492 }
493 }