Skip to content

Commit 5d45f3d

Browse files
committed
[GR-60490] Fix initial weight calculation of compilation tasks and prioritize tasks after invalidation with weight bonus.
PullRequest: graal/19628
2 parents e6ed2f0 + 26dab1c commit 5d45f3d

File tree

5 files changed

+70
-28
lines changed

5 files changed

+70
-28
lines changed

truffle/docs/Options.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ These are internal options for debugging language implementations and tools.
170170
- `--engine.TraversingCompilationQueue=true|false` : Use a traversing compilation queue. (default: true)
171171
- `--engine.TraversingQueueFirstTierBonus=[0.0, inf)` : Controls how much of a priority should be given to first tier compilations (default 15.0).
172172
- `--engine.TraversingQueueFirstTierPriority` : Traversing queue gives first tier compilations priority.
173+
- `--engine.TraversingQueueInvalidatedBonus=[0.0, inf)` : Controls how much of a priority should be given to compilations after invalidations (default: 1.0, no bonus).
173174
- `--engine.TraversingQueueWeightingBothTiers=true|false` : Traversing queue uses rate as priority for both tier. (default: true)
174175
- `--compiler.DiagnoseFailure` : Forces diagnostics for compilation failures (default: false).
175176
- `--compiler.ExcludeAssertions` : Exclude assertion code from Truffle compilations (default: true)

truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/CompilationTask.java

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -41,6 +41,8 @@
4141
package com.oracle.truffle.runtime;
4242

4343
import java.lang.ref.WeakReference;
44+
import java.util.ArrayList;
45+
import java.util.List;
4446
import java.util.concurrent.Callable;
4547
import java.util.concurrent.CancellationException;
4648
import java.util.concurrent.ExecutionException;
@@ -91,12 +93,23 @@ private CompilationTask(BackgroundCompileQueue.Priority priority, WeakReference<
9193
this.targetRef = targetRef;
9294
this.action = action;
9395
this.id = id;
94-
OptimizedCallTarget target = targetRef.get();
95-
lastCount = target != null ? target.getCallAndLoopCount() : Integer.MIN_VALUE;
96+
// Last weight < 0 indicates that this task's weight has not been initialized yet.
97+
// Why not initialize/calculate it here? We do not have any information about the rate of
98+
// change of its call and loop count yet since the task is newly created. Consequently, we
99+
// cannot compute a reliable rate yet. The rate will be computed the first time updateWeight
100+
// is called.
101+
lastWeight = -1.0;
96102
lastTime = System.nanoTime();
97-
lastWeight = target != null ? target.getCallAndLoopCount() : -1;
98-
engineData = target != null ? target.engine : null;
99-
isOSR = target != null && target.isOSR();
103+
OptimizedCallTarget target = targetRef.get();
104+
if (target == null) {
105+
lastCount = Integer.MIN_VALUE;
106+
engineData = null;
107+
isOSR = false;
108+
} else {
109+
lastCount = target.getCallAndLoopCount();
110+
engineData = target.engine;
111+
isOSR = target.isOSR();
112+
}
100113
}
101114

102115
static CompilationTask createInitializationTask(WeakReference<OptimizedCallTarget> targetRef, Consumer<CompilationTask> action) {
@@ -239,9 +252,9 @@ public Void call() throws Exception {
239252
* corrupt a queue data structure.
240253
*/
241254
boolean isHigherPriorityThan(CompilationTask other) {
242-
if (action != COMPILATION_ACTION) {
255+
if (action != COMPILATION_ACTION || other.action != COMPILATION_ACTION) {
243256
// Any non-compilation action (e.g. compiler init) is higher priority.
244-
return true;
257+
return action != COMPILATION_ACTION;
245258
}
246259
if ((this.isOSR ^ other.isOSR) && this.isLastTier() && other.isLastTier()) {
247260
// Any OSR task has higher priority than any non-OSR last tier task
@@ -251,12 +264,6 @@ boolean isHigherPriorityThan(CompilationTask other) {
251264
if (engineData.traversingFirstTierPriority && tier != other.tier()) {
252265
return tier < other.tier();
253266
}
254-
int otherCompileTier = other.targetHighestCompiledTier();
255-
int compiledTier = targetHighestCompiledTier();
256-
if (tier == other.tier() && compiledTier != otherCompileTier) {
257-
// tasks previously compiled with higher tier are better
258-
return compiledTier > otherCompileTier;
259-
}
260267
if (engineData.weightingBothTiers || isFirstTier()) {
261268
return lastWeight > other.lastWeight;
262269
}
@@ -272,17 +279,25 @@ boolean updateWeight(long currentTime) {
272279
return false;
273280
}
274281
long elapsed = currentTime - lastTime;
275-
if (elapsed < 1_000_000) {
282+
// A last weight > 0 indicates that it has been initialized. If so, only update its weight
283+
// if 1_000_000ns have elapsed since its last calculation. The elapsed time may be negative
284+
// since tasks may be added to the queue while traversing it.
285+
if (lastWeight > 0 && elapsed < 1_000_000 || elapsed < 0) {
276286
return true;
277287
}
278288
int count = target.getCallAndLoopCount();
279289
lastRate = rate(count, elapsed);
280290
lastTime = currentTime;
281291
lastCount = count;
282292
double weight = (1 + lastRate) * lastCount;
283-
if (engineData.traversingFirstTierPriority) {
284-
lastWeight = weight;
285-
} else {
293+
assert weight >= 0.0 : "weight must be positive";
294+
lastWeight = weight * bonus();
295+
return true;
296+
}
297+
298+
private double bonus() {
299+
double bonus = 1.0;
300+
if (!engineData.traversingFirstTierPriority && isFirstTier()) {
286301
// @formatter:off
287302
// We multiply first tier compilations with this bonus to bring first and last tier
288303
// compilation weights to roughly the same order of magnitude and give first tier compilations some priority.
@@ -296,15 +311,31 @@ boolean updateWeight(long currentTime) {
296311
// This controls for the fact that weight is a multiple of the callAndLoopCount and this
297312
// count is on the order of the thresholds which is much smaller for first tier compilations
298313
// @formatter:on
299-
lastWeight = weight * (isFirstTier() ? engineData.traversingFirstTierBonus : 1);
314+
bonus *= engineData.traversingFirstTierBonus;
300315
}
301-
assert weight >= 0.0 : "weight must be positive";
302-
return true;
316+
if (targetHighestCompiledTier() >= tier()) {
317+
// If the task has already been compiled with the same or a higher tier, implying the
318+
// previous compilation has been invalidated, boost its priority.
319+
// This bonus is 1.0 by default, i.e. it has no effect.
320+
bonus *= engineData.traversingInvalidatedBonus;
321+
}
322+
return bonus;
323+
}
324+
325+
public List<String> bonusDescriptors() {
326+
List<String> bonuses = new ArrayList<>(2);
327+
if (!engineData.traversingFirstTierPriority && isFirstTier()) {
328+
bonuses.add("first tier");
329+
}
330+
if (targetHighestCompiledTier() >= tier()) {
331+
bonuses.add("invalidation");
332+
}
333+
return bonuses;
303334
}
304335

305336
private double rate(int count, long elapsed) {
306-
lastRate = ((double) count - lastCount) / elapsed;
307-
return (Double.isNaN(lastRate) ? 0 : lastRate);
337+
// Divide by a minimum of 1000 to prevent division by zero/very small numbers.
338+
return lastRate = ((double) count - lastCount) / Math.max(elapsed, 1000);
308339
}
309340

310341
public int targetHighestCompiledTier() {

truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/EngineData.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -78,6 +78,7 @@
7878
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceTransferToInterpreter;
7979
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraversingQueueFirstTierBonus;
8080
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraversingQueueFirstTierPriority;
81+
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraversingQueueInvalidatedBonus;
8182
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraversingQueueWeightingBothTiers;
8283
import static com.oracle.truffle.runtime.OptimizedTruffleRuntime.getRuntime;
8384

@@ -158,6 +159,7 @@ public final class EngineData {
158159
@CompilationFinal public boolean weightingBothTiers;
159160
@CompilationFinal public boolean traversingFirstTierPriority;
160161
@CompilationFinal public double traversingFirstTierBonus;
162+
@CompilationFinal public double traversingInvalidatedBonus;
161163
@CompilationFinal public boolean propagateCallAndLoopCount;
162164
@CompilationFinal public int propagateCallAndLoopCountMaxDepth;
163165
@CompilationFinal public int maximumCompilations;
@@ -319,6 +321,7 @@ private void loadOptions(OptionValues options, SandboxPolicy sandboxPolicy) {
319321
// See usage of traversingFirstTierBonus for explanation of this formula.
320322
traversingFirstTierBonus = options.get(TraversingQueueFirstTierBonus) * options.get(LastTierCompilationThreshold) / options.get(FirstTierCompilationThreshold);
321323
maximumCompilations = options.get(MaximumCompilations);
324+
traversingInvalidatedBonus = options.get(TraversingQueueInvalidatedBonus);
322325

323326
this.returnTypeSpeculation = options.get(ReturnTypeSpeculation);
324327
this.argumentTypeSpeculation = options.get(ArgumentTypeSpeculation);

truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedRuntimeOptions.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -333,6 +333,9 @@ public EngineModeEnum apply(String s) {
333333
@Option(help = "Traversing queue gives first tier compilations priority.", category = OptionCategory.INTERNAL) //
334334
public static final OptionKey<Boolean> TraversingQueueFirstTierPriority = new OptionKey<>(false);
335335

336+
@Option(help = "Controls how much of a priority should be given to compilations after invalidations (default: 1.0, no bonus).", usageSyntax = "[0.0, inf)", category = OptionCategory.INTERNAL) //
337+
public static final OptionKey<Double> TraversingQueueInvalidatedBonus = new OptionKey<>(1.0);
338+
336339
@Option(help = "Traversing queue uses rate as priority for both tier. (default: true)", usageSyntax = "true|false", category = OptionCategory.INTERNAL) //
337340
public static final OptionKey<Boolean> TraversingQueueWeightingBothTiers = new OptionKey<>(true);
338341

truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/debug/TraceCompilationListener.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -97,7 +97,7 @@ public static void install(OptimizedTruffleRuntime runtime) {
9797
// @formatter:off
9898
private static final String QUEUED_FORMAT = "opt queued " + TARGET_FORMAT + "|" + TIER_FORMAT + "|" + COUNT_THRESHOLD_FORMAT + "|" + QUEUE_FORMAT + "|UTC %s|Src %s";
9999
private static final String UNQUEUED_FORMAT = "opt unque. " + TARGET_FORMAT + "|" + TIER_FORMAT + "|" + COUNT_THRESHOLD_FORMAT + "|" + QUEUE_FORMAT + "|UTC %s|Src %s|Reason %s";
100-
private static final String START_FORMAT = "opt start " + TARGET_FORMAT + "|" + TIER_FORMAT + "|Priority %9d|Rate %.6f|" + QUEUE_FORMAT + "|UTC %s|Src %s";
100+
private static final String START_FORMAT = "opt start " + TARGET_FORMAT + "|" + TIER_FORMAT + "|Priority %9d|Rate %.6f|" + QUEUE_FORMAT + "|UTC %s|Src %s|Bonuses %s";
101101
private static final String DONE_FORMAT = "opt done " + TARGET_FORMAT + "|" + TIER_FORMAT + "|Time %18s|AST %4d|Inlined %3dY %3dN|IR %6d/%6d|CodeSize %7d|Addr 0x%012x|CompId %-7s|UTC %s|Src %s";
102102
private static final String FAILED_FORMAT = "opt failed " + TARGET_FORMAT + "|" + TIER_FORMAT + "|Time %18s|Reason: %s|UTC %s|Src %s";
103103
private static final String PADDING = " ";
@@ -181,16 +181,19 @@ public void onCompilationStarted(OptimizedCallTarget target, AbstractCompilation
181181
long time;
182182
double rate;
183183
int queueChange;
184+
String appliedBonuses;
184185
if (task instanceof CompilationTask t) {
185186
weight = t.weight();
186187
time = t.time();
187188
rate = t.rate();
188189
queueChange = t.queueChange();
190+
appliedBonuses = String.join(", ", t.bonusDescriptors());
189191
} else {
190192
weight = 0.0d;
191193
time = 0;
192194
rate = Double.NaN;
193195
queueChange = 0;
196+
appliedBonuses = "";
194197
}
195198
log(target, String.format(START_FORMAT,
196199
target.engineId(),
@@ -205,7 +208,8 @@ public void onCompilationStarted(OptimizedCallTarget target, AbstractCompilation
205208
FixedPointMath.toDouble(runtime.compilationThresholdScale()),
206209
time / 1000,
207210
TIME_FORMATTER.format(ZonedDateTime.now()),
208-
formatSourceSection(safeSourceSection(target))));
211+
formatSourceSection(safeSourceSection(target)),
212+
appliedBonuses));
209213
}
210214

211215
if (target.engine.traceCompilation || target.engine.traceCompilationDetails) {

0 commit comments

Comments
 (0)