Skip to content

Commit dd2c3a2

Browse files
committed
[GR-60636] Implement Truffle compiler control based on HotSpot's CompileBroker compilation activity.
PullRequest: graal/19658
2 parents 7456cd5 + e0ab6c0 commit dd2c3a2

File tree

10 files changed

+176
-8
lines changed

10 files changed

+176
-8
lines changed

sdk/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
This changelog summarizes major changes between GraalVM SDK versions. The main focus is on APIs exported by GraalVM SDK.
44

5+
## Version 25.0.0
6+
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.
7+
58
## Version 24.2.0
69
* GR-54905 When using Truffle NFI with the Panama backend, native access must now be granted to the Truffle module instead of the NFI Panama module. Use the `--enable-native-access=org.graalvm.truffle` Java command line option to enable the native access for the NFI Panama backend.
710
* GR-57681 Added the ability to use `Value#as(byte[].class)` to copy the contents of a guest language byte buffer (`Value#hasBufferElements()`) to a new byte array. The new functionality has precedence over accessing the guest object as array (`Value#hasArrayElements()`) if both ways are available.

truffle/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
This changelog summarizes major changes between Truffle versions relevant to languages implementors building upon the Truffle framework. The main focus is on APIs exported by Truffle.
44

5+
## Version 24.2.0
6+
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.
7+
58
## Version 24.2.0
69
* GR-57658 Added `TruffleLanguage.Env.getLanguageInfo(Class<? extends TruffleLanguage>)` to lookup a `LanguageInfo` instance for a language class returned by `InteropLibrary.getLanguage(Object)`.
710
* GR-57164 Added support for reading unaligned ints, shorts and long to `ByteArraySupport`.

truffle/docs/Options.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ The accepted values are:
8181
- `--engine.PartialBlockMaximumSize=[1, inf)` : Sets the maximum non-trivial Truffle node size for partial compilation of BlockNode nodes (default: 10000).
8282
- `--engine.SingleTierCompilationThreshold=[1, inf)` : Minimum number of invocations or loop iterations needed to compile a guest language root when not using multi tier (default: 1000).
8383
- `--engine.Splitting=true|false` : Enable automatic duplication of compilation profiles (splitting) (default: true).
84+
- `--engine.StoppedCompilationRetryDelay=<ms>` : Before the Truffle runtime submits an OptimizedCallTarget for compilation, it checks for the compilation activity mode in the host VM. If the activity mode indicates a full code cache, no new compilation requests are submitted and the compilation queue is flushed. After 'StoppedCompilationRetryDelay' milliseconds new compilations will be submitted again (which might trigger a sweep of the code cache and a reset of the compilation activity mode in the host JVM). The option is only supported on the HotSpot Truffle runtime. On runtimes which don't support it the option has no effect. default: 5000
8485
- `--engine.TraceCompilation` : Print information for compilation results.
8586
- `--compiler.EncodedGraphCache` : Cache encoded graphs across Truffle compilations to speed up partial evaluation. (default: true).
8687
- `--compiler.FirstTierUseEconomy` : Whether to use the economy configuration in the first-tier compilations. (default: true, syntax: true|false)

truffle/src/com.oracle.truffle.compiler/src/com/oracle/truffle/compiler/TruffleCompilerRuntime.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2024, 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
@@ -358,5 +358,4 @@ default ResolvedJavaType resolveType(MetaAccessProvider metaAccess, String class
358358
* silent.
359359
*/
360360
boolean isSuppressedFailure(TruffleCompilable compilable, Supplier<String> serializedException);
361-
362361
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2024, 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
@@ -218,7 +218,8 @@ public int getQueueSize() {
218218

219219
/**
220220
* Return call targets waiting in queue. This does not include call targets currently being
221-
* compiled.
221+
* compiled. If {@code engine} is {@code null}, the call targets for all engines are returned,
222+
* otherwise only the call targets belonging to {@code engine} will be returned.
222223
*/
223224
public Collection<OptimizedCallTarget> getQueuedTargets(EngineData engine) {
224225
BlockingQueue<Runnable> queue = this.compilationQueue;
@@ -230,7 +231,7 @@ public Collection<OptimizedCallTarget> getQueuedTargets(EngineData engine) {
230231
CompilationTask.ExecutorServiceWrapper[] array = queue.toArray(new CompilationTask.ExecutorServiceWrapper[0]);
231232
for (CompilationTask.ExecutorServiceWrapper wrapper : array) {
232233
OptimizedCallTarget target = wrapper.compileTask.targetRef.get();
233-
if (target != null && target.engine == engine) {
234+
if (target != null && (engine == null || target.engine == engine)) {
234235
queuedTargets.add(target);
235236
}
236237
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingMaxCalleeSize;
6969
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingMaxPropagationDepth;
7070
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingTraceEvents;
71+
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.StoppedCompilationRetryDelay;
7172
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceCompilation;
7273
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceCompilationDetails;
7374
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceDeoptimizeFrame;
@@ -85,6 +86,7 @@
8586
import java.util.Map;
8687
import java.util.Objects;
8788
import java.util.concurrent.ConcurrentHashMap;
89+
import java.util.concurrent.atomic.AtomicBoolean;
8890
import java.util.function.Function;
8991
import java.util.logging.Level;
9092

@@ -148,6 +150,7 @@ public final class EngineData {
148150
@CompilationFinal public boolean traceDeoptimizeFrame;
149151
@CompilationFinal public boolean compileAOTOnCreate;
150152
@CompilationFinal public boolean firstTierOnly;
153+
@CompilationFinal public long stoppedCompilationRetryDelay;
151154

152155
// compilation queue options
153156
@CompilationFinal public boolean priorityQueue;
@@ -305,6 +308,7 @@ private void loadOptions(OptionValues options, SandboxPolicy sandboxPolicy) {
305308
this.firstTierOnly = options.get(Mode) == EngineModeEnum.LATENCY;
306309
this.propagateCallAndLoopCount = options.get(PropagateLoopCountToLexicalSingleCaller);
307310
this.propagateCallAndLoopCountMaxDepth = options.get(PropagateLoopCountToLexicalSingleCallerMaxDepth);
311+
this.stoppedCompilationRetryDelay = options.get(StoppedCompilationRetryDelay);
308312

309313
// compilation queue options
310314
priorityQueue = options.get(PriorityQueue);
@@ -492,6 +496,16 @@ public TruffleLogger getLogger(String loggerId) {
492496
return polyglotEngine != null ? loggerFactory.apply(loggerId) : null;
493497
}
494498

499+
private final AtomicBoolean logShutdownCompilations = new AtomicBoolean(true);
500+
501+
/**
502+
* Only log compilation shutdowns (see {@code OptimizedCallTarget.isCompilationStopped()}) once
503+
* per engine.
504+
*/
505+
public AtomicBoolean logShutdownCompilations() {
506+
return logShutdownCompilations;
507+
}
508+
495509
@SuppressWarnings("static-method")
496510
public void mergeLoadedSources(Source[] sources) {
497511
OptimizedRuntimeAccessor.SOURCE.mergeLoadedSources(sources);

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.lang.ref.Reference;
4444
import java.lang.ref.WeakReference;
4545
import java.util.ArrayList;
46+
import java.util.Collection;
4647
import java.util.LinkedHashMap;
4748
import java.util.List;
4849
import java.util.Map;
@@ -51,7 +52,9 @@
5152
import java.util.concurrent.atomic.AtomicLong;
5253
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
5354
import java.util.function.Supplier;
55+
import java.util.logging.Level;
5456

57+
import com.oracle.truffle.api.TruffleLogger;
5558
import org.graalvm.options.OptionKey;
5659
import org.graalvm.options.OptionValues;
5760

@@ -82,6 +85,7 @@
8285
import com.oracle.truffle.compiler.TruffleCompilable;
8386
import com.oracle.truffle.runtime.OptimizedBlockNode.PartialBlockRootNode;
8487
import com.oracle.truffle.runtime.OptimizedRuntimeOptions.ExceptionAction;
88+
import com.oracle.truffle.runtime.OptimizedTruffleRuntime.CompilationActivityMode;
8589

8690
import jdk.vm.ci.meta.JavaConstant;
8791
import jdk.vm.ci.meta.SpeculationLog;
@@ -897,6 +901,58 @@ final boolean isCompilationFailed() {
897901
return compilationFailed;
898902
}
899903

904+
private CompilationActivityMode getCompilationActivityMode() {
905+
CompilationActivityMode compilationActivityMode = runtime().getCompilationActivityMode();
906+
long stoppedTime = runtime().stoppedCompilationTime().get();
907+
if (compilationActivityMode == CompilationActivityMode.STOP_COMPILATION) {
908+
if (stoppedTime != 0 && System.currentTimeMillis() - stoppedTime > engine.stoppedCompilationRetryDelay) {
909+
runtime().stoppedCompilationTime().compareAndSet(stoppedTime, 0);
910+
// Try again every StoppedCompilationRetryDelay milliseconds to potentially trigger
911+
// a code cache sweep.
912+
compilationActivityMode = CompilationActivityMode.RUN_COMPILATION;
913+
}
914+
}
915+
916+
switch (compilationActivityMode) {
917+
case RUN_COMPILATION:
918+
// This is the common case - compilations are not stopped.
919+
return CompilationActivityMode.RUN_COMPILATION;
920+
case STOP_COMPILATION:
921+
if (stoppedTime == 0) {
922+
runtime().stoppedCompilationTime().compareAndSet(0, System.currentTimeMillis());
923+
}
924+
// Flush the compilations queue for now. There's still a chance that compilation
925+
// will be re-enabled eventually, if the hosts code cache can be cleaned up.
926+
Collection<OptimizedCallTarget> targets = runtime().getCompileQueue().getQueuedTargets(null);
927+
// If there's just a single compilation target in the queue, the chance is high that
928+
// it is the one we've just added after the StoppedCompilationRetryDelay ran out, so
929+
// keep it to potentially trigger a code cache sweep.
930+
if (targets.size() > 1) {
931+
for (OptimizedCallTarget target : targets) {
932+
target.cancelCompilation("Compilation temporary disabled due to full code cache.");
933+
}
934+
}
935+
return CompilationActivityMode.STOP_COMPILATION;
936+
case SHUTDOWN_COMPILATION:
937+
// Compilation was shut down permanently because the hosts code cache ran full and
938+
// the host was configured without support for code cache sweeping.
939+
TruffleLogger logger = engine.getLogger("engine");
940+
// The logger can be null if the engine is closed.
941+
if (logger != null && engine.logShutdownCompilations().compareAndExchange(true, false)) {
942+
logger.log(Level.WARNING, "Truffle compilations permanently disabled because of full code cache. " +
943+
"Increase the code cache size using '-XX:ReservedCodeCacheSize=' and/or run with '-XX:+UseCodeCacheFlushing -XX:+MethodFlushing'.");
944+
}
945+
// Flush the compilation queue and mark all methods as not compilable.
946+
for (OptimizedCallTarget target : runtime().getCompileQueue().getQueuedTargets(null)) {
947+
target.cancelCompilation("Compilation permanently disabled due to full code cache.");
948+
target.compilationFailed = true;
949+
}
950+
return CompilationActivityMode.SHUTDOWN_COMPILATION;
951+
default:
952+
throw CompilerDirectives.shouldNotReachHere("Invalid compilation activity mode: " + compilationActivityMode);
953+
}
954+
}
955+
900956
/**
901957
* Returns <code>true</code> if the call target was already compiled or was compiled
902958
* synchronously. Returns <code>false</code> if compilation was not scheduled or is happening in
@@ -908,13 +964,23 @@ public final boolean compile(boolean lastTierCompilation) {
908964
if (!needsCompile(lastTier)) {
909965
return true;
910966
}
967+
911968
if (!isSubmittedForCompilation()) {
912969
if (!acceptForCompilation()) {
913970
// do not try to compile again
914971
compilationFailed = true;
915972
return false;
916973
}
917974

975+
CompilationActivityMode cam = getCompilationActivityMode();
976+
if (cam != CompilationActivityMode.RUN_COMPILATION) {
977+
if (cam == CompilationActivityMode.SHUTDOWN_COMPILATION) {
978+
// Compilation was shut down permanently.
979+
compilationFailed = true;
980+
}
981+
return false;
982+
}
983+
918984
CompilationTask task = null;
919985
// Do not try to compile this target concurrently,
920986
// but do not block other threads if compilation is not asynchronous.

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2024, 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
@@ -158,6 +158,15 @@ public ExceptionAction apply(String s) {
158158
// TODO: GR-29949
159159
public static final OptionKey<Long> CompilerIdleDelay = new OptionKey<>(10000L);
160160

161+
@Option(help = "Before the Truffle runtime submits an OptimizedCallTarget for compilation, it checks for the compilation " +
162+
"activity mode in the host VM. If the activity mode indicates a full code cache, no new compilation " +
163+
"requests are submitted and the compilation queue is flushed. After 'StoppedCompilationRetryDelay' " +
164+
"milliseconds new compilations will be submitted again (which might trigger a sweep of the code " +
165+
"cache and a reset of the compilation activity mode in the host JVM). The option is only supported on " +
166+
"the HotSpot Truffle runtime. On runtimes which don't support it the option has no effect. default: 5000", //
167+
usageSyntax = "<ms>", category = OptionCategory.EXPERT) //
168+
public static final OptionKey<Long> StoppedCompilationRetryDelay = new OptionKey<>(5000L);
169+
161170
@Option(help = "Manually set the number of compiler threads. By default, the number of compiler threads is scaled with the number of available cores on the CPU.", usageSyntax = "[1, inf)", category = OptionCategory.EXPERT, //
162171
stability = OptionStability.STABLE, sandbox = SandboxPolicy.UNTRUSTED) //
163172
public static final OptionKey<Integer> CompilerThreads = new OptionKey<>(-1);

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

Lines changed: 29 additions & 1 deletion
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, 2024, 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
@@ -63,6 +63,7 @@
6363
import java.util.concurrent.ExecutionException;
6464
import java.util.concurrent.TimeUnit;
6565
import java.util.concurrent.TimeoutException;
66+
import java.util.concurrent.atomic.AtomicLong;
6667
import java.util.function.Consumer;
6768
import java.util.function.Supplier;
6869
import java.util.logging.Level;
@@ -912,6 +913,12 @@ private void notifyCompilationFailure(OptimizedCallTarget callTarget, Throwable
912913
protected void onEngineCreated(EngineData engine) {
913914
}
914915

916+
private final AtomicLong stoppedCompilationTime = new AtomicLong(0);
917+
918+
public final AtomicLong stoppedCompilationTime() {
919+
return stoppedCompilationTime;
920+
}
921+
915922
@SuppressWarnings("try")
916923
public CompilationTask submitForCompilation(OptimizedCallTarget optimizedCallTarget, boolean lastTierCompilation) {
917924
Priority priority = new Priority(optimizedCallTarget.getCallAndLoopCount(), lastTierCompilation ? Priority.Tier.LAST : Priority.Tier.FIRST);
@@ -1483,4 +1490,25 @@ static OptionCategory matchCategory(TruffleCompilerOptionDescriptor d) {
14831490
}
14841491
}
14851492

1493+
public enum CompilationActivityMode {
1494+
/**
1495+
* Process compilations regularly.
1496+
*/
1497+
RUN_COMPILATION,
1498+
/**
1499+
* Stop compilations temporarily.
1500+
*/
1501+
STOP_COMPILATION,
1502+
/**
1503+
* Shutdown compilations permanently.
1504+
*/
1505+
SHUTDOWN_COMPILATION;
1506+
}
1507+
1508+
/**
1509+
* Returns the current host compilation activity mode. The default is to run compilations.
1510+
*/
1511+
protected CompilationActivityMode getCompilationActivityMode() {
1512+
return CompilationActivityMode.RUN_COMPILATION;
1513+
}
14861514
}

truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/hotspot/HotSpotTruffleRuntime.java

Lines changed: 45 additions & 1 deletion
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, 2024, 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
@@ -40,6 +40,9 @@
4040
*/
4141
package com.oracle.truffle.runtime.hotspot;
4242

43+
import java.lang.invoke.MethodHandle;
44+
import java.lang.invoke.MethodHandles;
45+
import java.lang.invoke.MethodType;
4346
import java.lang.ref.Reference;
4447
import java.lang.reflect.Field;
4548
import java.lang.reflect.InvocationTargetException;
@@ -692,4 +695,45 @@ public boolean isLibGraalCompilationEnabled() {
692695
return compilationSupport instanceof LibGraalTruffleCompilationSupport;
693696
}
694697

698+
private static final MethodHandle getCompilationActivityMode;
699+
static {
700+
MethodHandle mHandle = null;
701+
try {
702+
MethodType mt = MethodType.methodType(int.class);
703+
mHandle = MethodHandles.lookup().findVirtual(HotSpotJVMCIRuntime.class, "getCompilationActivityMode", mt);
704+
} catch (NoSuchMethodException | IllegalAccessException e) {
705+
// Older JVMCI runtimes might not support `getCompilationActivityMode()`
706+
}
707+
getCompilationActivityMode = mHandle;
708+
}
709+
710+
/**
711+
* Returns the current host compilation activity mode based on HotSpot's code cache state.
712+
*/
713+
@Override
714+
protected CompilationActivityMode getCompilationActivityMode() {
715+
int activityMode = 1; // Default is to run compilations
716+
if (getCompilationActivityMode != null) {
717+
try {
718+
activityMode = (int) getCompilationActivityMode.invokeExact(HotSpotJVMCIRuntime.runtime());
719+
} catch (Throwable t) {
720+
throw CompilerDirectives.shouldNotReachHere("Can't get HotSpot's compilation activity mode", t);
721+
}
722+
}
723+
return resolveHotSpotActivityMode(activityMode);
724+
}
725+
726+
/**
727+
* Represents HotSpot's compilation activity mode which is one of: {@code stop_compilation = 0},
728+
* {@code run_compilation = 1} or {@code shutdown_compilation = 2}. Should be in sync with the
729+
* {@code CompilerActivity} enum in {@code hotspot/share/compiler/compileBroker.hpp}.
730+
*/
731+
private static CompilationActivityMode resolveHotSpotActivityMode(int i) {
732+
return switch (i) {
733+
case 0 -> CompilationActivityMode.STOP_COMPILATION;
734+
case 1 -> CompilationActivityMode.RUN_COMPILATION;
735+
case 2 -> CompilationActivityMode.SHUTDOWN_COMPILATION;
736+
default -> throw CompilerDirectives.shouldNotReachHere("Invalid CompilationActivityMode " + i);
737+
};
738+
}
695739
}

0 commit comments

Comments
 (0)