JMH threading model can support only FixedThreadPoolExecutor.
All other existing options (CACHED_TPE, FJP_COMMON, and CUSTOM ( which is not fixed)) are not working.
The problem is the following.
At the beginning of benchmark execution, inside BenchmarkHandler constructor, a set of ThreadParams is created:
tps = new ArrayBlockingQueue<>(executionParams.getThreads());
tps.addAll(distributeThreads(executionParams.getThreads(), executionParams.getThreadGroups()));
Assigning ThreadParams to Thread happens inside newWorkerData (called from BenchmarkTask.call()).
Every iteration BenchmarkTasks are submitted to the executor:
CompletionService<BenchmarkTaskResult> srv = new ExecutorCompletionService<>(executor);
for (BenchmarkTask runner : runners) {
srv.submit(runner);
}
If, on the second (or later) iteration, the executor submits benchmark task to the thread which was not used on the first iteration - we are getting an error. The new thread doesn't have associated WorkerData, and "tps" queue is already drained.
That was observed on:
- CACHED_TPE (CachedThreadPool doesn't have a guarantee that threads survive between submit loops on different iterations)
- FJP_COMMON, was observed on JDK 11 (17, 21 are fine), but also, there is no guarantee that submitted tasks will be assigned to the same set of threads as on the first iteration.
It was not observed on FJP, but theoretically may happen when the benchmark uses ManagedBlockers.
It can't work for CUSTOM executor until the custom executor is FixedThreadPool.
All other existing options (CACHED_TPE, FJP_COMMON, and CUSTOM ( which is not fixed)) are not working.
The problem is the following.
At the beginning of benchmark execution, inside BenchmarkHandler constructor, a set of ThreadParams is created:
tps = new ArrayBlockingQueue<>(executionParams.getThreads());
tps.addAll(distributeThreads(executionParams.getThreads(), executionParams.getThreadGroups()));
Assigning ThreadParams to Thread happens inside newWorkerData (called from BenchmarkTask.call()).
Every iteration BenchmarkTasks are submitted to the executor:
CompletionService<BenchmarkTaskResult> srv = new ExecutorCompletionService<>(executor);
for (BenchmarkTask runner : runners) {
srv.submit(runner);
}
If, on the second (or later) iteration, the executor submits benchmark task to the thread which was not used on the first iteration - we are getting an error. The new thread doesn't have associated WorkerData, and "tps" queue is already drained.
That was observed on:
- CACHED_TPE (CachedThreadPool doesn't have a guarantee that threads survive between submit loops on different iterations)
- FJP_COMMON, was observed on JDK 11 (17, 21 are fine), but also, there is no guarantee that submitted tasks will be assigned to the same set of threads as on the first iteration.
It was not observed on FJP, but theoretically may happen when the benchmark uses ManagedBlockers.
It can't work for CUSTOM executor until the custom executor is FixedThreadPool.
- relates to
-
CODETOOLS-7903490 JMH: The interrupt to time-outing benchmark can be delivered to worker data barrier
-
- Resolved
-
-
CODETOOLS-7903471 JMH: Add test for virtual threads executor
-
- Closed
-