Skip to content

Commit 9e46a60

Browse files
simonischumer
authored andcommitted
Implement Truffle compiler control based on HotSpot's CompileBroker compilation activity
1 parent 5a5cb09 commit 9e46a60

File tree

7 files changed

+164
-8
lines changed

7 files changed

+164
-8
lines changed

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: 70 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

@@ -81,6 +84,7 @@
8184
import com.oracle.truffle.api.nodes.RootNode;
8285
import com.oracle.truffle.compiler.TruffleCompilable;
8386
import com.oracle.truffle.runtime.OptimizedRuntimeOptions.ExceptionAction;
87+
import com.oracle.truffle.runtime.OptimizedTruffleRuntime.CompilationActivityMode;
8488

8589
import jdk.vm.ci.meta.JavaConstant;
8690
import jdk.vm.ci.meta.SpeculationLog;
@@ -855,6 +859,62 @@ final boolean isCompilationFailed() {
855859
return compilationFailed;
856860
}
857861

862+
final CompilationActivityMode getCompilationActivityMode() {
863+
CompilationActivityMode compilationActivityMode = runtime().getCompilationActivityMode();
864+
long sct = runtime().stoppedCompilationTime().get();
865+
if (compilationActivityMode == CompilationActivityMode.STOP_COMPILATION) {
866+
if (sct != 0 && System.currentTimeMillis() - sct > engine.stoppedCompilationRetryDelay) {
867+
runtime().stoppedCompilationTime().compareAndSet(sct, 0);
868+
// Try again every StoppedCompilationRetryDelay milliseconds to potentially trigger
869+
// a code cache sweep.
870+
compilationActivityMode = CompilationActivityMode.RUN_COMPILATION;
871+
}
872+
}
873+
874+
switch (compilationActivityMode) {
875+
case RUN_COMPILATION: {
876+
// This is the common case - compilations are not stopped.
877+
return CompilationActivityMode.RUN_COMPILATION;
878+
}
879+
case STOP_COMPILATION: {
880+
if (sct == 0) {
881+
runtime().stoppedCompilationTime().compareAndSet(0, System.currentTimeMillis());
882+
}
883+
// Flush the compilations queue for now. There's still a chance that compilation
884+
// will be re-enabled eventually, if the hosts code cache can be cleaned up.
885+
Collection<OptimizedCallTarget> targets = runtime().getCompileQueue().getQueuedTargets(null);
886+
// If there's just a single compilation target in the queue, the chance is high that
887+
// it is the one we've just added after the StoppedCompilationRetryDelay ran out, so
888+
// keep it to potentially trigger a code cache sweep.
889+
if (targets.size() > 1) {
890+
for (OptimizedCallTarget target : targets) {
891+
target.cancelCompilation("Compilation temporary disabled due to full code cache.");
892+
}
893+
}
894+
return CompilationActivityMode.STOP_COMPILATION;
895+
}
896+
case SHUTDOWN_COMPILATION: {
897+
// Compilation was shut down permanently because the hosts code cache ran full and
898+
// the host was configured without support for code cache sweeping.
899+
TruffleLogger logger = engine.getLogger("engine");
900+
// The logger can be null if the engine is closed.
901+
if (logger != null && engine.logShutdownCompilations().compareAndExchange(true, false)) {
902+
logger.log(Level.WARNING, "Truffle compilations permanently disabled because of full code cache. " +
903+
"Increase the code cache size using '-XX:ReservedCodeCacheSize=' and/or run with '-XX:+UseCodeCacheFlushing -XX:+MethodFlushing'.");
904+
}
905+
// Flush the compilation queue and mark all methods as not compilable.
906+
for (OptimizedCallTarget target : runtime().getCompileQueue().getQueuedTargets(null)) {
907+
target.cancelCompilation("Compilation permanently disabled due to full code cache.");
908+
target.compilationFailed = true;
909+
}
910+
return CompilationActivityMode.SHUTDOWN_COMPILATION;
911+
}
912+
default:
913+
CompilerDirectives.shouldNotReachHere("Invalid compilation activity mode: " + compilationActivityMode);
914+
}
915+
return CompilationActivityMode.RUN_COMPILATION;
916+
}
917+
858918
/**
859919
* Returns <code>true</code> if the call target was already compiled or was compiled
860920
* synchronously. Returns <code>false</code> if compilation was not scheduled or is happening in
@@ -866,13 +926,23 @@ public final boolean compile(boolean lastTierCompilation) {
866926
if (!needsCompile(lastTier)) {
867927
return true;
868928
}
929+
869930
if (!isSubmittedForCompilation()) {
870931
if (!acceptForCompilation()) {
871932
// do not try to compile again
872933
compilationFailed = true;
873934
return false;
874935
}
875936

937+
CompilationActivityMode cam = getCompilationActivityMode();
938+
if (cam != CompilationActivityMode.RUN_COMPILATION) {
939+
if (cam == CompilationActivityMode.SHUTDOWN_COMPILATION) {
940+
// Compilation was shut down permanently.
941+
compilationFailed = true;
942+
}
943+
return false;
944+
}
945+
876946
CompilationTask task = null;
877947
// Do not try to compile this target concurrently,
878948
// 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: 34 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,30 @@ static OptionCategory matchCategory(TruffleCompilerOptionDescriptor d) {
14831490
}
14841491
}
14851492

1493+
/**
1494+
* Represents HotSpot's compilation activity mode which is one of: {@code stop_compilation = 0},
1495+
* {@code run_compilation = 1} or {@code shutdown_compilation = 2}. Should be in sync with the
1496+
* {@code CompilerActivity} enum in {@code hotspot/share/compiler/compileBroker.hpp}.
1497+
*/
1498+
public enum CompilationActivityMode {
1499+
STOP_COMPILATION,
1500+
RUN_COMPILATION,
1501+
SHUTDOWN_COMPILATION;
1502+
1503+
public static CompilationActivityMode fromInteger(int i) {
1504+
return switch (i) {
1505+
case 0 -> STOP_COMPILATION;
1506+
case 1 -> RUN_COMPILATION;
1507+
case 2 -> SHUTDOWN_COMPILATION;
1508+
default -> throw new RuntimeException("Invalid CompilationActivityMode " + i);
1509+
};
1510+
}
1511+
}
1512+
1513+
/**
1514+
* Returns the current host compilation activity mode. The default is to run compilations.
1515+
*/
1516+
public CompilationActivityMode getCompilationActivityMode() {
1517+
return CompilationActivityMode.RUN_COMPILATION;
1518+
}
14861519
}

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

Lines changed: 31 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,31 @@ public boolean isLibGraalCompilationEnabled() {
692695
return compilationSupport instanceof LibGraalTruffleCompilationSupport;
693696
}
694697

698+
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+
public 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 new RuntimeException("Can't get HotSpot's compilation activity mode", t);
721+
}
722+
}
723+
return CompilationActivityMode.fromInteger(activityMode);
724+
}
695725
}

0 commit comments

Comments
 (0)