Skip to content

Commit 547b494

Browse files
committed
Use a concurrent map to manage timings
When using a large number of concurrent steps, writing timings back can become a contention point as it locks the CpsFlowExecution object. Replaced the timings implementation by a concurrent map and make sure all operations are atomic.
1 parent d0a8f6a commit 547b494

File tree

1 file changed

+15
-30
lines changed

1 file changed

+15
-30
lines changed

src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecution.java

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
import java.util.NavigableMap;
9393
import java.util.Stack;
9494
import java.util.TreeMap;
95+
import java.util.concurrent.ConcurrentHashMap;
9596
import java.util.concurrent.CopyOnWriteArrayList;
9697
import java.util.concurrent.ExecutionException;
9798
import java.util.concurrent.Future;
@@ -384,8 +385,7 @@ enum TimingKind {
384385
}
385386

386387
/** accumulated time in ns of a given {@link TimingKind#name}; {@link String} key for pretty XStream form */
387-
@GuardedBy("this")
388-
@CheckForNull Map<String, Long> timings;
388+
@NonNull Map<String, Long> timings = new ConcurrentHashMap<>();
389389

390390
@Deprecated
391391
public CpsFlowExecution(String script, FlowExecutionOwner owner) throws IOException {
@@ -413,6 +413,10 @@ public CpsFlowExecution(String script, boolean sandbox, FlowExecutionOwner owner
413413
private Object readResolve() {
414414
if (loadedScripts==null)
415415
loadedScripts = new HashMap<>(); // field added later
416+
// Convert timings to concurrent hash map
417+
if (!(timings instanceof ConcurrentHashMap)) {
418+
timings = timings == null ? new ConcurrentHashMap<>() : new ConcurrentHashMap<>(timings);
419+
}
416420
return this;
417421
}
418422

@@ -424,16 +428,7 @@ private Timing(TimingKind kind) {
424428
start = System.nanoTime();
425429
}
426430
@Override public void close() {
427-
synchronized (CpsFlowExecution.this) {
428-
if (timings == null) {
429-
timings = new HashMap<>();
430-
}
431-
Long orig = timings.get(kind.name());
432-
if (orig == null) {
433-
orig = 0L;
434-
}
435-
timings.put(kind.name(), orig + System.nanoTime() - start);
436-
}
431+
timings.merge(kind.name(), System.nanoTime() - start, Long::sum);
437432
}
438433
}
439434

@@ -448,12 +443,10 @@ Timing time(TimingKind kind) {
448443

449444
static final Logger TIMING_LOGGER = Logger.getLogger(CpsFlowExecution.class.getName() + ".timing");
450445

451-
synchronized void logTimings() {
452-
if (timings != null && TIMING_LOGGER.isLoggable(Level.FINE)) {
446+
void logTimings() {
447+
if (TIMING_LOGGER.isLoggable(Level.FINE)) {
453448
Map<String, String> formatted = new TreeMap<>();
454-
for (Map.Entry<String, Long> entry : timings.entrySet()) {
455-
formatted.put(entry.getKey(), entry.getValue() / 1000 / 1000 + "ms");
456-
}
449+
timings.forEach((k, v) -> formatted.put(k, v / 1000 / 1000 + "ms"));
457450
TIMING_LOGGER.log(Level.FINE, "timings for {0}: {1}", new Object[] {owner, formatted});
458451
}
459452
}
@@ -1643,11 +1636,7 @@ public void marshal(Object source, HierarchicalStreamWriter w, MarshallingContex
16431636
if (e.durabilityHint != null) {
16441637
writeChild(w, context, "durabilityHint", e.durabilityHint, FlowDurabilityHint.class);
16451638
}
1646-
synchronized (e) {
1647-
if (e.timings != null) {
1648-
writeChild(w, context, "timings", e.timings, Map.class);
1649-
}
1650-
}
1639+
writeChild(w, context, "timings", e.timings, Map.class);
16511640
writeChild(w, context, "sandbox", e.sandbox, Boolean.class);
16521641
if (e.user != null) {
16531642
writeChild(w, context, "user", e.user, String.class);
@@ -1931,14 +1920,10 @@ public void autopersist(@NonNull FlowNode n) throws IOException {
19311920
continue;
19321921
}
19331922
if (exec instanceof CpsFlowExecution) {
1934-
Map<String, Long> timings = ((CpsFlowExecution) exec).timings;
1935-
if (timings != null) {
1936-
pw.println("Timings for " + run + ":");
1937-
for (Map.Entry<String, Long> entry : new TreeMap<>(timings).entrySet()) {
1938-
pw.println(" " + entry.getKey() + "\t" + entry.getValue() / 1000 / 1000 + "ms");
1939-
}
1940-
pw.println();
1941-
}
1923+
Map<String, Long> sortedTimings = new TreeMap<>(((CpsFlowExecution) exec).timings);
1924+
pw.println("Timings for " + run + ":");
1925+
sortedTimings.forEach((k, v) -> pw.println(" " + k + "\t" + v / 1000 / 1000 + "ms"));
1926+
pw.println();
19421927
}
19431928
}
19441929
}

0 commit comments

Comments
 (0)