1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.buffer;
17
18 import org.jfree.chart.ChartFactory;
19 import org.jfree.chart.JFreeChart;
20 import org.jfree.data.category.DefaultCategoryDataset;
21
22 import java.awt.BasicStroke;
23 import java.awt.Rectangle;
24 import java.awt.event.ComponentAdapter;
25 import java.awt.event.ComponentEvent;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.List;
29 import java.util.SplittableRandom;
30 import java.util.concurrent.CountDownLatch;
31 import java.util.concurrent.atomic.AtomicBoolean;
32 import java.util.stream.Collectors;
33 import java.util.stream.LongStream;
34 import java.util.stream.Stream;
35 import javax.swing.ImageIcon;
36 import javax.swing.JFrame;
37 import javax.swing.JLabel;
38 import javax.swing.SwingUtilities;
39 import javax.swing.WindowConstants;
40
41 public class AllocationPatternSimulator {
42
43
44
45 private static final int[] WEB_SOCKET_PROXY_PATTERN = {
46
47 9, 316,
48 13, 3,
49 15, 10344,
50 17, 628,
51 21, 316,
52 36, 338,
53 48, 338,
54 64, 23,
55 128, 17,
56 256, 21272,
57 287, 69,
58 304, 65,
59 331, 11,
60 332, 7,
61 335, 2,
62 343, 2,
63 362, 1,
64 363, 16,
65 365, 17,
66 370, 11,
67 371, 51,
68 392, 11,
69 393, 4,
70 396, 3,
71 401, 1,
72 402, 3,
73 413, 1,
74 414, 2,
75 419, 16,
76 421, 1,
77 423, 16,
78 424, 46,
79 433, 1,
80 435, 1,
81 439, 3,
82 441, 13,
83 444, 3,
84 449, 1,
85 450, 1,
86 453, 2,
87 455, 3,
88 458, 3,
89 462, 7,
90 463, 8,
91 464, 1,
92 466, 59,
93 470, 1,
94 472, 2,
95 475, 1,
96 478, 2,
97 480, 12,
98 481, 16,
99 482, 2,
100 483, 2,
101 486, 1,
102 489, 2,
103 493, 2,
104 494, 1,
105 495, 1,
106 497, 14,
107 498, 1,
108 499, 2,
109 500, 58,
110 503, 1,
111 507, 1,
112 509, 2,
113 510, 2,
114 511, 13,
115 512, 3,
116 513, 4,
117 516, 1,
118 519, 2,
119 520, 1,
120 522, 5,
121 523, 1,
122 525, 15,
123 526, 1,
124 527, 55,
125 528, 2,
126 529, 1,
127 530, 1,
128 531, 3,
129 533, 1,
130 534, 1,
131 535, 1,
132 536, 10,
133 538, 4,
134 539, 3,
135 540, 2,
136 541, 1,
137 542, 3,
138 543, 10,
139 545, 5,
140 546, 1,
141 547, 14,
142 548, 1,
143 549, 53,
144 551, 1,
145 552, 1,
146 553, 1,
147 554, 1,
148 555, 2,
149 556, 11,
150 557, 3,
151 558, 7,
152 559, 4,
153 561, 3,
154 562, 1,
155 563, 6,
156 564, 3,
157 565, 13,
158 566, 31,
159 567, 24,
160 568, 1,
161 569, 1,
162 570, 4,
163 571, 2,
164 572, 9,
165 573, 7,
166 574, 3,
167 575, 2,
168 576, 4,
169 577, 2,
170 578, 7,
171 579, 12,
172 580, 38,
173 581, 22,
174 582, 1,
175 583, 3,
176 584, 5,
177 585, 9,
178 586, 9,
179 587, 6,
180 588, 3,
181 589, 5,
182 590, 8,
183 591, 23,
184 592, 42,
185 593, 3,
186 594, 5,
187 595, 11,
188 596, 10,
189 597, 7,
190 598, 5,
191 599, 13,
192 600, 26,
193 601, 41,
194 602, 8,
195 603, 14,
196 604, 18,
197 605, 14,
198 606, 16,
199 607, 35,
200 608, 57,
201 609, 74,
202 610, 13,
203 611, 24,
204 612, 22,
205 613, 52,
206 614, 88,
207 615, 28,
208 616, 23,
209 617, 37,
210 618, 70,
211 619, 74,
212 620, 31,
213 621, 59,
214 622, 110,
215 623, 37,
216 624, 67,
217 625, 110,
218 626, 55,
219 627, 140,
220 628, 71,
221 629, 141,
222 630, 141,
223 631, 147,
224 632, 190,
225 633, 254,
226 634, 349,
227 635, 635,
228 636, 5443,
229 637, 459,
230 639, 1,
231 640, 2,
232 642, 1,
233 644, 2,
234 645, 1,
235 647, 1,
236 649, 1,
237 650, 1,
238 652, 1,
239 655, 3,
240 656, 1,
241 658, 4,
242 659, 2,
243 660, 1,
244 661, 1,
245 662, 6,
246 663, 8,
247 664, 9,
248 665, 4,
249 666, 5,
250 667, 62,
251 668, 5,
252 693, 1,
253 701, 2,
254 783, 1,
255 941, 1,
256 949, 1,
257 958, 16,
258 988, 1,
259 1024, 29289,
260 1028, 1,
261 1086, 1,
262 1249, 2,
263 1263, 1,
264 1279, 24,
265 1280, 11,
266 1309, 1,
267 1310, 1,
268 1311, 2,
269 1343, 1,
270 1360, 2,
271 1483, 1,
272 1567, 1,
273 1957, 1,
274 2048, 2636,
275 2060, 1,
276 2146, 1,
277 2190, 1,
278 2247, 1,
279 2273, 1,
280 2274, 1,
281 2303, 106,
282 2304, 45,
283 2320, 1,
284 2333, 10,
285 2334, 14,
286 2335, 7,
287 2367, 7,
288 2368, 2,
289 2384, 7,
290 2399, 1,
291 2400, 14,
292 2401, 6,
293 2423, 1,
294 2443, 9,
295 2444, 1,
296 2507, 3,
297 3039, 1,
298 3140, 1,
299 3891, 1,
300 3893, 1,
301 4096, 26,
302 4118, 1,
303 4321, 1,
304 4351, 226,
305 4352, 15,
306 4370, 1,
307 4381, 1,
308 4382, 11,
309 4383, 10,
310 4415, 4,
311 4416, 3,
312 4432, 5,
313 4447, 1,
314 4448, 31,
315 4449, 14,
316 4471, 1,
317 4491, 42,
318 4492, 16,
319 4555, 26,
320 4556, 19,
321 4571, 1,
322 4572, 2,
323 4573, 53,
324 4574, 165,
325 5770, 1,
326 5803, 2,
327 6026, 1,
328 6144, 2,
329 6249, 1,
330 6278, 1,
331 6466, 1,
332 6680, 1,
333 6726, 2,
334 6728, 1,
335 6745, 1,
336 6746, 1,
337 6759, 1,
338 6935, 1,
339 6978, 1,
340 6981, 2,
341 6982, 1,
342 7032, 1,
343 7081, 1,
344 7086, 1,
345 7110, 1,
346 7172, 3,
347 7204, 2,
348 7236, 2,
349 7238, 1,
350 7330, 1,
351 7427, 3,
352 7428, 1,
353 7458, 1,
354 7459, 1,
355 7650, 2,
356 7682, 6,
357 7765, 1,
358 7937, 3,
359 7969, 1,
360 8192, 2,
361 8415, 1,
362 8447, 555,
363 8478, 3,
364 8479, 5,
365 8511, 2,
366 8512, 1,
367 8528, 1,
368 8543, 2,
369 8544, 9,
370 8545, 8,
371 8567, 1,
372 8587, 16,
373 8588, 12,
374 8650, 1,
375 8651, 9,
376 8652, 9,
377 8668, 3,
378 8669, 46,
379 8670, 195,
380 8671, 6,
381 10240, 4,
382 14336, 1,
383 14440, 4,
384 14663, 3,
385 14919, 1,
386 14950, 2,
387 15002, 1,
388 15159, 1,
389 15173, 2,
390 15205, 1,
391 15395, 1,
392 15396, 1,
393 15397, 2,
394 15428, 1,
395 15446, 1,
396 15619, 7,
397 15651, 5,
398 15683, 2,
399 15874, 8,
400 15906, 8,
401 15907, 2,
402 16128, 2,
403 16129, 37,
404 16161, 3,
405 16352, 2,
406 16383, 1,
407 16384, 42,
408 16610, 2,
409 16639, 9269,
410 16704, 2,
411 16736, 3,
412 16737, 2,
413 16779, 2,
414 16780, 7,
415 16843, 2,
416 16844, 5,
417 16860, 6,
418 16861, 67,
419 16862, 281,
420 16863, 13,
421 18432, 6,
422 };
423 private static final int CONCURRENCY_LEVEL = 4;
424 private static final int RUNNING_TIME_SECONDS = 120;
425
426 AdaptiveByteBufAllocator adaptive128;
427 PooledByteBufAllocator pooled128;
428 AdaptiveByteBufAllocator adaptive2048;
429 PooledByteBufAllocator pooled2048;
430 int[] size;
431 int[] cumulativeFrequency;
432 int sumFrequency;
433 int count;
434
435 public static void main(String[] args) throws Exception {
436 AllocationPatternSimulator runner = new AllocationPatternSimulator();
437 runner.setUp(WEB_SOCKET_PROXY_PATTERN);
438 runner.run(CONCURRENCY_LEVEL, RUNNING_TIME_SECONDS);
439 }
440
441 void setUp(int[] pattern) {
442 adaptive128 = new AdaptiveByteBufAllocator();
443 pooled128 = new PooledByteBufAllocator();
444 adaptive2048 = new AdaptiveByteBufAllocator();
445 pooled2048 = new PooledByteBufAllocator();
446 PatternItr itr = new PatternItr();
447 size = new int[pattern.length >> 1];
448 cumulativeFrequency = new int[pattern.length >> 1];
449 sumFrequency = 0;
450 count = 0;
451 while (itr.next()) {
452 sumFrequency += itr.frequency();
453 size[count] = itr.size();
454 cumulativeFrequency[count] = sumFrequency;
455 count++;
456 }
457 }
458
459 void run(int concurrencyLevel, int runningTimeSeconds) throws Exception {
460 AllocConfig[] allocs = {
461 new AllocConfig(true, 128),
462 new AllocConfig(false, 128),
463 new AllocConfig(true, 512),
464 new AllocConfig(false, 512),
465 new AllocConfig(true, 1024),
466 new AllocConfig(false, 1024),
467 };
468
469 CountDownLatch startLatch = new CountDownLatch(1);
470 AtomicBoolean stopCondition = new AtomicBoolean();
471 List<Thread> threads = new ArrayList<>();
472
473 for (int i = 0; i < concurrencyLevel; i++) {
474 for (AllocConfig alloc : allocs) {
475 threads.add(alloc.start(startLatch, stopCondition));
476 }
477 }
478
479 DefaultCategoryDataset dataset = new DefaultCategoryDataset();
480 JFreeChart chart = ChartFactory.createLineChart("Memory Usage", "Time", "Bytes", dataset);
481 for (int i = 0; i < allocs.length; i++) {
482 chart.getCategoryPlot().getRenderer().setSeriesStroke(i, new BasicStroke(3.0f));
483 }
484 int windowWidth = 1400;
485 int windowHeight = 1050;
486 ImageIcon image = new ImageIcon(chart.createBufferedImage(windowWidth, windowHeight - 30));
487
488 JFrame frame = new JFrame("Results");
489 frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
490 frame.add(new JLabel(image));
491 frame.setBounds(0, 0, windowWidth, windowHeight);
492 frame.setVisible(true);
493 Runnable updateImage = () -> {
494 Rectangle bounds = frame.getBounds();
495 image.setImage(chart.createBufferedImage(bounds.width, bounds.height - 30));
496 frame.repaint();
497 };
498 frame.addComponentListener(new ComponentAdapter() {
499 @Override
500 public void componentResized(ComponentEvent e) {
501 updateImage.run();
502 }
503 });
504
505 startLatch.countDown();
506
507 System.out.println("Time," + Stream.of(allocs)
508 .map(AllocConfig::name)
509 .collect(Collectors.joining("\",\"", "\"", "\"")));
510 for (int i = 0; i < runningTimeSeconds; i++) {
511 Thread.sleep(1000);
512 Integer iteration = Integer.valueOf(i);
513
514 long[] usages = new long[allocs.length];
515 for (int j = 0; j < usages.length; j++) {
516 usages[j] = allocs[j].usedMemory();
517 }
518 System.out.println(iteration + "," + LongStream.of(usages)
519 .mapToObj(String::valueOf).collect(Collectors.joining(",")));
520 SwingUtilities.invokeLater(() -> {
521 for (int j = 0; j < usages.length; j++) {
522 dataset.addValue(usages[j], allocs[j].name(), iteration);
523 }
524 updateImage.run();
525 });
526 }
527
528 stopCondition.set(true);
529
530 for (Thread thread : threads) {
531 thread.join();
532 }
533 System.out.println("\nDone");
534 }
535
536 private final class AllocConfig {
537 private final AbstractByteBufAllocator allocator;
538 private final int avgLiveBufs;
539 private final SplittableRandom rng;
540 private final String name;
541
542 AllocConfig(boolean isAdaptive, int avgLiveBufs) {
543 allocator = isAdaptive ? new AdaptiveByteBufAllocator() : new PooledByteBufAllocator();
544 name = String.format(isAdaptive ? "Adaptive (%s)" : "Pooled (%s)", avgLiveBufs);
545 this.avgLiveBufs = avgLiveBufs;
546 rng = new SplittableRandom(0xBEEFBEEFL);
547 }
548
549 Thread start(CountDownLatch startLatch, AtomicBoolean stopCondition) {
550 return new Workload(startLatch, allocator, rng.split(), stopCondition, avgLiveBufs).start();
551 }
552
553 long usedMemory() {
554 if (allocator instanceof AdaptiveByteBufAllocator) {
555 return ((AdaptiveByteBufAllocator) allocator).usedHeapMemory();
556 }
557 return ((PooledByteBufAllocator) allocator).usedHeapMemory();
558 }
559
560 String name() {
561 return name;
562 }
563 }
564
565 private final class Workload implements Runnable {
566 private final CountDownLatch startLatch;
567 private final ByteBufAllocator allocator;
568 private final SplittableRandom rng;
569 private final AtomicBoolean stopCondition;
570 private final int avgLiveBuffers;
571
572 private Workload(CountDownLatch startLatch,
573 ByteBufAllocator allocator,
574 SplittableRandom rng,
575 AtomicBoolean stopCondition,
576 int avgLiveBuffers) {
577 this.startLatch = startLatch;
578 this.allocator = allocator;
579 this.rng = rng;
580 this.stopCondition = stopCondition;
581 this.avgLiveBuffers = avgLiveBuffers;
582 }
583
584 Thread start() {
585 Thread thread = new Thread(this);
586 thread.start();
587 return thread;
588 }
589
590 @SuppressWarnings("BusyWait")
591 @Override
592 public void run() {
593 try {
594 startLatch.await();
595 List<ByteBuf> buffers = new ArrayList<>();
596 while (!stopCondition.get()) {
597 Thread.sleep(1);
598
599 int freqChoice = rng.nextInt(0, sumFrequency);
600 int choiceIndex = Arrays.binarySearch(cumulativeFrequency, freqChoice);
601 if (choiceIndex < 0) {
602 choiceIndex = -(choiceIndex + 1);
603 }
604 int chosenSize = size[choiceIndex];
605 ByteBuf buf = allocator.heapBuffer(chosenSize);
606 buffers.add(buf);
607 while (buffers.size() > rng.nextInt(0, avgLiveBuffers << 1)) {
608 int deallocChoice = rng.nextInt(0, buffers.size());
609 ByteBuf toDealloc = buffers.get(deallocChoice);
610 ByteBuf lastBuffer = buffers.remove(buffers.size() - 1);
611 if (toDealloc != lastBuffer) {
612 buffers.set(deallocChoice, lastBuffer);
613 }
614 toDealloc.release();
615 }
616 }
617 } catch (InterruptedException e) {
618 throw new RuntimeException(e);
619 }
620 }
621 }
622
623 private static final class PatternItr {
624 private int index;
625 private boolean hasData;
626 private int size;
627 private int frequency;
628
629 public boolean next() {
630 if (index < WEB_SOCKET_PROXY_PATTERN.length) {
631 size = WEB_SOCKET_PROXY_PATTERN[index++];
632 frequency = WEB_SOCKET_PROXY_PATTERN[index++];
633 return hasData = true;
634 }
635 return hasData = false;
636 }
637
638 public int size() {
639 if (!hasData) {
640 throw new IllegalStateException();
641 }
642 return size;
643 }
644
645 public int frequency() {
646 if (!hasData) {
647 throw new IllegalStateException();
648 }
649 return frequency;
650 }
651 }
652 }