|
9 | 9 |
|
10 | 10 | package org.elasticsearch.benchmark.common.util.concurrent; |
11 | 11 |
|
12 | | -import org.elasticsearch.common.settings.Settings; |
13 | | -import org.elasticsearch.common.util.concurrent.EsExecutors; |
14 | | -import org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor; |
15 | 12 | import org.elasticsearch.common.util.concurrent.TaskExecutionTimeTrackingEsThreadPoolExecutor; |
16 | | -import org.elasticsearch.common.util.concurrent.ThreadContext; |
17 | 13 | import org.openjdk.jmh.annotations.Benchmark; |
18 | 14 | import org.openjdk.jmh.annotations.BenchmarkMode; |
19 | 15 | import org.openjdk.jmh.annotations.Fork; |
| 16 | +import org.openjdk.jmh.annotations.Group; |
20 | 17 | import org.openjdk.jmh.annotations.Measurement; |
21 | 18 | import org.openjdk.jmh.annotations.Mode; |
22 | 19 | import org.openjdk.jmh.annotations.OutputTimeUnit; |
23 | 20 | import org.openjdk.jmh.annotations.Param; |
24 | 21 | import org.openjdk.jmh.annotations.Scope; |
25 | 22 | import org.openjdk.jmh.annotations.Setup; |
26 | 23 | import org.openjdk.jmh.annotations.State; |
27 | | -import org.openjdk.jmh.annotations.TearDown; |
| 24 | +import org.openjdk.jmh.annotations.Threads; |
28 | 25 | import org.openjdk.jmh.annotations.Warmup; |
29 | 26 | import org.openjdk.jmh.infra.Blackhole; |
30 | 27 |
|
31 | | -import java.time.Duration; |
32 | | -import java.util.concurrent.CountDownLatch; |
33 | | -import java.util.concurrent.Executors; |
34 | 28 | import java.util.concurrent.TimeUnit; |
35 | 29 |
|
36 | | -@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) |
37 | | -@Measurement(iterations = 1, time = 10, timeUnit = TimeUnit.SECONDS) |
| 30 | +@Threads(12) |
| 31 | +@Warmup(iterations = 3, time = 200, timeUnit = TimeUnit.MILLISECONDS) |
| 32 | +@Measurement(iterations = 5, time = 600, timeUnit = TimeUnit.MILLISECONDS) |
38 | 33 | @BenchmarkMode(Mode.AverageTime) |
39 | | -@OutputTimeUnit(TimeUnit.MILLISECONDS) |
| 34 | +@OutputTimeUnit(TimeUnit.MICROSECONDS) |
| 35 | +@State(Scope.Benchmark) |
40 | 36 | @Fork(1) |
41 | | -@State(Scope.Thread) |
42 | 37 | public class ThreadPoolUtilizationBenchmark { |
43 | 38 |
|
44 | | - @Param({ "false", "true" }) |
45 | | - private boolean trackUtilization; |
| 39 | + @Param({ "0", "10000", "100000" }) |
| 40 | + private int callIntervalTicks; |
46 | 41 |
|
47 | | - @Param({ "4", "8", "16" }) |
48 | | - private int poolSize; |
49 | | - |
50 | | - @Param({ "1000000" }) |
51 | | - private int tasksNum; |
52 | | - |
53 | | - @Param({ "10" }) // 10ms is aggressive interval, it increases frame updates on FramedTimeTracker, normally we run at 30/60 |
54 | | - // seconds |
| 42 | + /** |
| 43 | + * This makes very little difference, all the overhead is in the synchronization |
| 44 | + */ |
| 45 | + @Param({ "10" }) |
55 | 46 | private int utilizationIntervalMs; |
56 | 47 |
|
57 | | - private EsThreadPoolExecutor executor; |
| 48 | + @State(Scope.Thread) |
| 49 | + public static class TaskState { |
| 50 | + boolean running = false; |
58 | 51 |
|
59 | | - private EsThreadPoolExecutor newExecutor(boolean tracking) { |
60 | | - var conf = EsExecutors.TaskTrackingConfig.builder(); |
61 | | - if (tracking) { |
62 | | - conf.trackExecutionTime(0.3).trackUtilization(Duration.ofMillis(utilizationIntervalMs)); |
| 52 | + boolean shouldStart() { |
| 53 | + return (running = running == false); |
63 | 54 | } |
64 | | - return EsExecutors.newFixed( |
65 | | - "bench", |
66 | | - poolSize, |
67 | | - tasksNum, |
68 | | - Executors.defaultThreadFactory(), |
69 | | - new ThreadContext(Settings.EMPTY), |
70 | | - conf.build() |
71 | | - ); |
72 | 55 | } |
73 | 56 |
|
| 57 | + private TaskExecutionTimeTrackingEsThreadPoolExecutor.FramedTimeTracker timeTracker; |
| 58 | + |
74 | 59 | @Setup |
75 | 60 | public void setup() { |
76 | | - if (trackUtilization) { |
77 | | - var exec = newExecutor(true); |
78 | | - if (exec instanceof TaskExecutionTimeTrackingEsThreadPoolExecutor trackingExecutor) { |
79 | | - if (trackingExecutor.trackingConfig().trackUtilization() == false) { |
80 | | - throw new IllegalStateException("utilization tracking must be enabled"); |
81 | | - } else { |
82 | | - executor = trackingExecutor; |
83 | | - } |
84 | | - } else { |
85 | | - throw new IllegalStateException("must be tracking executor"); |
86 | | - } |
| 61 | + timeTracker = new TaskExecutionTimeTrackingEsThreadPoolExecutor.FramedTimeTracker( |
| 62 | + TimeUnit.MILLISECONDS.toNanos(utilizationIntervalMs), |
| 63 | + System::nanoTime |
| 64 | + ); |
| 65 | + } |
| 66 | + |
| 67 | + @Benchmark |
| 68 | + public void baseline() { |
| 69 | + Blackhole.consumeCPU(callIntervalTicks); |
| 70 | + } |
| 71 | + |
| 72 | + @Group("ReadAndWrite") |
| 73 | + @Benchmark |
| 74 | + public void startAndStopTasks(TaskState state) { |
| 75 | + Blackhole.consumeCPU(callIntervalTicks); |
| 76 | + if (state.shouldStart()) { |
| 77 | + timeTracker.startTask(); |
87 | 78 | } else { |
88 | | - var exec = newExecutor(false); |
89 | | - if (exec instanceof TaskExecutionTimeTrackingEsThreadPoolExecutor) { |
90 | | - throw new IllegalStateException("must be non-tracking executor"); |
91 | | - } |
92 | | - executor = exec; |
| 79 | + timeTracker.endTask(); |
93 | 80 | } |
94 | 81 | } |
95 | 82 |
|
96 | | - @TearDown |
97 | | - public void tearDown() throws InterruptedException { |
98 | | - executor.shutdown(); |
99 | | - executor.awaitTermination(0, TimeUnit.MILLISECONDS); |
| 83 | + @Benchmark |
| 84 | + @Group("ReadAndWrite") |
| 85 | + public void readPrevious(Blackhole blackhole) { |
| 86 | + Blackhole.consumeCPU(callIntervalTicks); |
| 87 | + blackhole.consume(timeTracker.previousFrameTime()); |
100 | 88 | } |
101 | 89 |
|
102 | 90 | @Benchmark |
103 | | - public void run(Blackhole bh) throws InterruptedException { |
104 | | - var completedTasks = new CountDownLatch(tasksNum); |
105 | | - for (var i = 0; i < tasksNum; i++) { |
106 | | - executor.execute(() -> { |
107 | | - // busy cycles for cpu |
108 | | - var r = 0; |
109 | | - for (var j = 0; j < 1000; j++) { |
110 | | - r += j * 2; |
111 | | - } |
112 | | - bh.consume(r); |
113 | | - completedTasks.countDown(); |
114 | | - }); |
| 91 | + @Group("JustWrite") |
| 92 | + public void startAndStopTasksOnly(TaskState state) { |
| 93 | + Blackhole.consumeCPU(callIntervalTicks); |
| 94 | + if (state.shouldStart()) { |
| 95 | + timeTracker.startTask(); |
| 96 | + } else { |
| 97 | + timeTracker.endTask(); |
115 | 98 | } |
116 | | - completedTasks.await(); |
117 | 99 | } |
118 | 100 | } |
0 commit comments