Skip to content

Time framed thread-pool utilization #131898

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
18ec741
framed-time-tracker
mhl-b Jul 25, 2025
80f792f
jmh
mhl-b Jul 25, 2025
8345da4
tests
mhl-b Jul 26, 2025
2421600
Merge remote-tracking branch 'upstream/main' into framed-thread-pool-…
mhl-b Jul 26, 2025
cb531b9
Update docs/changelog/131898.yaml
mhl-b Jul 26, 2025
ab04cc6
assertion fix
mhl-b Jul 26, 2025
deea172
Merge branch 'framed-thread-pool-utilization' of github.com:mhl-b/ela…
mhl-b Jul 26, 2025
454a871
duration toNanos
mhl-b Jul 26, 2025
5228abd
Merge remote-tracking branch 'upstream/main' into framed-thread-pool-…
mhl-b Jul 26, 2025
a87a7c4
comments
mhl-b Jul 26, 2025
8850789
[CI] Auto commit changes from spotless
Jul 26, 2025
3d66e86
Micro-ize the benchmark
nicktindall Jul 28, 2025
efa48b4
Use average instead of sample
nicktindall Jul 28, 2025
872d7cd
Merge pull request #2 from nicktindall/framed-thread-pool-utilization_bm
mhl-b Jul 29, 2025
33173fb
non-locking counting
mhl-b Jul 29, 2025
f3c81db
rwlock
mhl-b Jul 29, 2025
c281e8d
Merge remote-tracking branch 'upstream/main' into framed-thread-pool-…
mhl-b Jul 29, 2025
5771c46
cleanup
mhl-b Jul 29, 2025
12ef5e4
back to syncronized
mhl-b Jul 29, 2025
d006ed5
[CI] Auto commit changes from spotless
Jul 29, 2025
3caae03
Merge remote-tracking branch 'upstream/main' into framed-thread-pool-…
mhl-b Jul 29, 2025
18115c4
Merge branch 'framed-thread-pool-utilization' of github.com:mhl-b/ela…
mhl-b Jul 29, 2025
3816a50
non-locking frame windows
mhl-b Jul 30, 2025
fd0b91a
Merge remote-tracking branch 'upstream/main' into framed-thread-pool-…
mhl-b Jul 30, 2025
225280a
nits
mhl-b Jul 30, 2025
36374fc
window and frames
mhl-b Jul 31, 2025
3dd755e
[CI] Auto commit changes from spotless
Jul 31, 2025
0fbbd81
Merge remote-tracking branch 'upstream/main' into framed-thread-pool-…
mhl-b Jul 31, 2025
c1c33ac
fix
mhl-b Jul 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.benchmark.common.util.concurrent;

import org.elasticsearch.common.util.concurrent.TaskExecutionTimeTrackingEsThreadPoolExecutor;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Group;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.infra.Blackhole;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

@Threads(Threads.MAX)
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.MINUTES)
@State(Scope.Benchmark)
@Fork(1)
public class ThreadPoolUtilizationBenchmark {

@Param({ "10000" })
private int callIntervalTicks;

/**
* This makes very little difference, all the overhead is in the synchronization
*/
@Param({ "1000" })
private int frameDurationMs;

@Param({ "10000" })
private int reportingDurationMs;

private TaskExecutionTimeTrackingEsThreadPoolExecutor.FramedTimeTracker timeTracker;

@Setup
public void setup() {
timeTracker = new TaskExecutionTimeTrackingEsThreadPoolExecutor.FramedTimeTracker(
Duration.ofMillis(reportingDurationMs).toNanos(),
Duration.ofMillis(frameDurationMs).toNanos(),
System::nanoTime
);
}

@Benchmark
public void baseline() {
Blackhole.consumeCPU(callIntervalTicks);
}

@Group("StartAndEnd")
@Benchmark
public void startAndStopTasks() {
timeTracker.startTask();
Blackhole.consumeCPU(callIntervalTicks);
timeTracker.endTask();
}
}
5 changes: 5 additions & 0 deletions docs/changelog/131898.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 131898
summary: Time framed thread-pool utilization
area: Allocation
type: enhancement
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.node.Node;

import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.AbstractExecutorService;
Expand Down Expand Up @@ -576,80 +577,80 @@ public void rejectedExecution(Runnable task, ThreadPoolExecutor executor) {
}
}

public static class TaskTrackingConfig {
/**
* @param trackExecutionTime Whether to track execution stats
* @param trackUtilization Enables thread-pool utilization metrics
* @param utilizationReportingInterval When utilization is enabled, specifies interval for reporting utilization, default 30 seconds
* @param utilizationSamplingInterval When utilization is enabled, specifies sample interval, default 1 second
* @param trackOngoingTasks Whether to track ongoing task execution time, not just finished tasks
* @param trackMaxQueueLatency Whether to track max queue latency
* @param executionTimeEwmaAlpha The alpha seed for execution time EWMA (ExponentiallyWeightedMovingAverage)
*/
public record TaskTrackingConfig(
boolean trackExecutionTime,
boolean trackUtilization,
Duration utilizationReportingInterval,
Duration utilizationSamplingInterval,
boolean trackOngoingTasks,
boolean trackMaxQueueLatency,
double executionTimeEwmaAlpha
) {

// This is a random starting point alpha.
public static final double DEFAULT_EXECUTION_TIME_EWMA_ALPHA_FOR_TEST = 0.3;

private final boolean trackExecutionTime;
private final boolean trackOngoingTasks;
private final boolean trackMaxQueueLatency;
private final double executionTimeEwmaAlpha;
public static final Duration DEFAULT_UTILIZATION_INTERVAL = Duration.ofSeconds(30);
public static final Duration DEFAULT_UTILIZATION_SAMPLING_INTERVAL = Duration.ofSeconds(1);

public static final TaskTrackingConfig DO_NOT_TRACK = new TaskTrackingConfig(
false,
false,
DEFAULT_UTILIZATION_INTERVAL,
DEFAULT_UTILIZATION_SAMPLING_INTERVAL,
false,
false,
DEFAULT_EXECUTION_TIME_EWMA_ALPHA_FOR_TEST
);

public static final TaskTrackingConfig DEFAULT = new TaskTrackingConfig(
true,
true,
DEFAULT_UTILIZATION_INTERVAL,
DEFAULT_UTILIZATION_SAMPLING_INTERVAL,
false,
false,
DEFAULT_EXECUTION_TIME_EWMA_ALPHA_FOR_TEST
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is peripheral but I think we should move to using the builder for DEFAULT and DO_NOT_TRACK. All those flags are meaningless without mouseovers. More-so now that we've added another one.


/**
* @param trackExecutionTime Whether to track execution stats
* @param trackOngoingTasks Whether to track ongoing task execution time, not just finished tasks
* @param trackMaxQueueLatency Whether to track max queue latency.
* @param executionTimeEWMAAlpha The alpha seed for execution time EWMA (ExponentiallyWeightedMovingAverage).
*/
private TaskTrackingConfig(
boolean trackExecutionTime,
boolean trackOngoingTasks,
boolean trackMaxQueueLatency,
double executionTimeEWMAAlpha
) {
this.trackExecutionTime = trackExecutionTime;
this.trackOngoingTasks = trackOngoingTasks;
this.trackMaxQueueLatency = trackMaxQueueLatency;
this.executionTimeEwmaAlpha = executionTimeEWMAAlpha;
}

public boolean trackExecutionTime() {
return trackExecutionTime;
}

public boolean trackOngoingTasks() {
return trackOngoingTasks;
}

public boolean trackMaxQueueLatency() {
return trackMaxQueueLatency;
}

public double getExecutionTimeEwmaAlpha() {
return executionTimeEwmaAlpha;
}

public static Builder builder() {
return new Builder();
}

public static class Builder {
private boolean trackExecutionTime = false;
private boolean trackUtilization = false;
private boolean trackOngoingTasks = false;
private boolean trackMaxQueueLatency = false;
private double ewmaAlpha = DEFAULT_EXECUTION_TIME_EWMA_ALPHA_FOR_TEST;
private Duration utilizationInterval = DEFAULT_UTILIZATION_INTERVAL;
private Duration utilizationSamplingInterval = DEFAULT_UTILIZATION_SAMPLING_INTERVAL;

public Builder() {}

public Builder trackExecutionTime(double alpha) {
trackExecutionTime = true;
trackUtilization = true;
ewmaAlpha = alpha;
return this;
}

public Builder trackUtilization(Duration interval, Duration samplingInterval) {
assert interval.dividedBy(samplingInterval) > 0 : "interval should be same or larger than sampling interval";
trackUtilization = true;
utilizationInterval = interval;
utilizationSamplingInterval = samplingInterval;
return this;
}

public Builder trackOngoingTasks() {
trackOngoingTasks = true;
return this;
Expand All @@ -661,7 +662,15 @@ public Builder trackMaxQueueLatency() {
}

public TaskTrackingConfig build() {
return new TaskTrackingConfig(trackExecutionTime, trackOngoingTasks, trackMaxQueueLatency, ewmaAlpha);
return new TaskTrackingConfig(
trackExecutionTime,
trackUtilization,
utilizationInterval,
utilizationSamplingInterval,
trackOngoingTasks,
trackMaxQueueLatency,
ewmaAlpha
);
}
}
}
Expand Down
Loading