Skip to content

Commit

Permalink
[GR-60636] Implement Truffle compiler control based on HotSpot's Comp…
Browse files Browse the repository at this point in the history
…ileBroker compilation activity.

PullRequest: graal/19658
  • Loading branch information
chumer committed Dec 20, 2024
2 parents 7456cd5 + e0ab6c0 commit dd2c3a2
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 8 deletions.
3 changes: 3 additions & 0 deletions sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

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

## Version 25.0.0
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.

## Version 24.2.0
* 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.
* 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.
Expand Down
3 changes: 3 additions & 0 deletions truffle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

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.

## Version 24.2.0
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.

## Version 24.2.0
* GR-57658 Added `TruffleLanguage.Env.getLanguageInfo(Class<? extends TruffleLanguage>)` to lookup a `LanguageInfo` instance for a language class returned by `InteropLibrary.getLanguage(Object)`.
* GR-57164 Added support for reading unaligned ints, shorts and long to `ByteArraySupport`.
Expand Down
1 change: 1 addition & 0 deletions truffle/docs/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ The accepted values are:
- `--engine.PartialBlockMaximumSize=[1, inf)` : Sets the maximum non-trivial Truffle node size for partial compilation of BlockNode nodes (default: 10000).
- `--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).
- `--engine.Splitting=true|false` : Enable automatic duplication of compilation profiles (splitting) (default: true).
- `--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
- `--engine.TraceCompilation` : Print information for compilation results.
- `--compiler.EncodedGraphCache` : Cache encoded graphs across Truffle compilations to speed up partial evaluation. (default: true).
- `--compiler.FirstTierUseEconomy` : Whether to use the economy configuration in the first-tier compilations. (default: true, syntax: true|false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -358,5 +358,4 @@ default ResolvedJavaType resolveType(MetaAccessProvider metaAccess, String class
* silent.
*/
boolean isSuppressedFailure(TruffleCompilable compilable, Supplier<String> serializedException);

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -218,7 +218,8 @@ public int getQueueSize() {

/**
* Return call targets waiting in queue. This does not include call targets currently being
* compiled.
* compiled. If {@code engine} is {@code null}, the call targets for all engines are returned,
* otherwise only the call targets belonging to {@code engine} will be returned.
*/
public Collection<OptimizedCallTarget> getQueuedTargets(EngineData engine) {
BlockingQueue<Runnable> queue = this.compilationQueue;
Expand All @@ -230,7 +231,7 @@ public Collection<OptimizedCallTarget> getQueuedTargets(EngineData engine) {
CompilationTask.ExecutorServiceWrapper[] array = queue.toArray(new CompilationTask.ExecutorServiceWrapper[0]);
for (CompilationTask.ExecutorServiceWrapper wrapper : array) {
OptimizedCallTarget target = wrapper.compileTask.targetRef.get();
if (target != null && target.engine == engine) {
if (target != null && (engine == null || target.engine == engine)) {
queuedTargets.add(target);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingMaxCalleeSize;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingMaxPropagationDepth;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.SplittingTraceEvents;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.StoppedCompilationRetryDelay;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceCompilation;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceCompilationDetails;
import static com.oracle.truffle.runtime.OptimizedRuntimeOptions.TraceDeoptimizeFrame;
Expand All @@ -85,6 +86,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.logging.Level;

Expand Down Expand Up @@ -148,6 +150,7 @@ public final class EngineData {
@CompilationFinal public boolean traceDeoptimizeFrame;
@CompilationFinal public boolean compileAOTOnCreate;
@CompilationFinal public boolean firstTierOnly;
@CompilationFinal public long stoppedCompilationRetryDelay;

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

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

private final AtomicBoolean logShutdownCompilations = new AtomicBoolean(true);

/**
* Only log compilation shutdowns (see {@code OptimizedCallTarget.isCompilationStopped()}) once
* per engine.
*/
public AtomicBoolean logShutdownCompilations() {
return logShutdownCompilations;
}

@SuppressWarnings("static-method")
public void mergeLoadedSources(Source[] sources) {
OptimizedRuntimeAccessor.SOURCE.mergeLoadedSources(sources);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -51,7 +52,9 @@
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Supplier;
import java.util.logging.Level;

import com.oracle.truffle.api.TruffleLogger;
import org.graalvm.options.OptionKey;
import org.graalvm.options.OptionValues;

Expand Down Expand Up @@ -82,6 +85,7 @@
import com.oracle.truffle.compiler.TruffleCompilable;
import com.oracle.truffle.runtime.OptimizedBlockNode.PartialBlockRootNode;
import com.oracle.truffle.runtime.OptimizedRuntimeOptions.ExceptionAction;
import com.oracle.truffle.runtime.OptimizedTruffleRuntime.CompilationActivityMode;

import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.SpeculationLog;
Expand Down Expand Up @@ -897,6 +901,58 @@ final boolean isCompilationFailed() {
return compilationFailed;
}

private CompilationActivityMode getCompilationActivityMode() {
CompilationActivityMode compilationActivityMode = runtime().getCompilationActivityMode();
long stoppedTime = runtime().stoppedCompilationTime().get();
if (compilationActivityMode == CompilationActivityMode.STOP_COMPILATION) {
if (stoppedTime != 0 && System.currentTimeMillis() - stoppedTime > engine.stoppedCompilationRetryDelay) {
runtime().stoppedCompilationTime().compareAndSet(stoppedTime, 0);
// Try again every StoppedCompilationRetryDelay milliseconds to potentially trigger
// a code cache sweep.
compilationActivityMode = CompilationActivityMode.RUN_COMPILATION;
}
}

switch (compilationActivityMode) {
case RUN_COMPILATION:
// This is the common case - compilations are not stopped.
return CompilationActivityMode.RUN_COMPILATION;
case STOP_COMPILATION:
if (stoppedTime == 0) {
runtime().stoppedCompilationTime().compareAndSet(0, System.currentTimeMillis());
}
// Flush the compilations queue for now. There's still a chance that compilation
// will be re-enabled eventually, if the hosts code cache can be cleaned up.
Collection<OptimizedCallTarget> targets = runtime().getCompileQueue().getQueuedTargets(null);
// If there's just a single compilation target in the queue, the chance is high that
// it is the one we've just added after the StoppedCompilationRetryDelay ran out, so
// keep it to potentially trigger a code cache sweep.
if (targets.size() > 1) {
for (OptimizedCallTarget target : targets) {
target.cancelCompilation("Compilation temporary disabled due to full code cache.");
}
}
return CompilationActivityMode.STOP_COMPILATION;
case SHUTDOWN_COMPILATION:
// Compilation was shut down permanently because the hosts code cache ran full and
// the host was configured without support for code cache sweeping.
TruffleLogger logger = engine.getLogger("engine");
// The logger can be null if the engine is closed.
if (logger != null && engine.logShutdownCompilations().compareAndExchange(true, false)) {
logger.log(Level.WARNING, "Truffle compilations permanently disabled because of full code cache. " +
"Increase the code cache size using '-XX:ReservedCodeCacheSize=' and/or run with '-XX:+UseCodeCacheFlushing -XX:+MethodFlushing'.");
}
// Flush the compilation queue and mark all methods as not compilable.
for (OptimizedCallTarget target : runtime().getCompileQueue().getQueuedTargets(null)) {
target.cancelCompilation("Compilation permanently disabled due to full code cache.");
target.compilationFailed = true;
}
return CompilationActivityMode.SHUTDOWN_COMPILATION;
default:
throw CompilerDirectives.shouldNotReachHere("Invalid compilation activity mode: " + compilationActivityMode);
}
}

/**
* Returns <code>true</code> if the call target was already compiled or was compiled
* synchronously. Returns <code>false</code> if compilation was not scheduled or is happening in
Expand All @@ -908,13 +964,23 @@ public final boolean compile(boolean lastTierCompilation) {
if (!needsCompile(lastTier)) {
return true;
}

if (!isSubmittedForCompilation()) {
if (!acceptForCompilation()) {
// do not try to compile again
compilationFailed = true;
return false;
}

CompilationActivityMode cam = getCompilationActivityMode();
if (cam != CompilationActivityMode.RUN_COMPILATION) {
if (cam == CompilationActivityMode.SHUTDOWN_COMPILATION) {
// Compilation was shut down permanently.
compilationFailed = true;
}
return false;
}

CompilationTask task = null;
// Do not try to compile this target concurrently,
// but do not block other threads if compilation is not asynchronous.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -158,6 +158,15 @@ public ExceptionAction apply(String s) {
// TODO: GR-29949
public static final OptionKey<Long> CompilerIdleDelay = new OptionKey<>(10000L);

@Option(help = "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", //
usageSyntax = "<ms>", category = OptionCategory.EXPERT) //
public static final OptionKey<Long> StoppedCompilationRetryDelay = new OptionKey<>(5000L);

@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, //
stability = OptionStability.STABLE, sandbox = SandboxPolicy.UNTRUSTED) //
public static final OptionKey<Integer> CompilerThreads = new OptionKey<>(-1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -63,6 +63,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;
Expand Down Expand Up @@ -912,6 +913,12 @@ private void notifyCompilationFailure(OptimizedCallTarget callTarget, Throwable
protected void onEngineCreated(EngineData engine) {
}

private final AtomicLong stoppedCompilationTime = new AtomicLong(0);

public final AtomicLong stoppedCompilationTime() {
return stoppedCompilationTime;
}

@SuppressWarnings("try")
public CompilationTask submitForCompilation(OptimizedCallTarget optimizedCallTarget, boolean lastTierCompilation) {
Priority priority = new Priority(optimizedCallTarget.getCallAndLoopCount(), lastTierCompilation ? Priority.Tier.LAST : Priority.Tier.FIRST);
Expand Down Expand Up @@ -1483,4 +1490,25 @@ static OptionCategory matchCategory(TruffleCompilerOptionDescriptor d) {
}
}

public enum CompilationActivityMode {
/**
* Process compilations regularly.
*/
RUN_COMPILATION,
/**
* Stop compilations temporarily.
*/
STOP_COMPILATION,
/**
* Shutdown compilations permanently.
*/
SHUTDOWN_COMPILATION;
}

/**
* Returns the current host compilation activity mode. The default is to run compilations.
*/
protected CompilationActivityMode getCompilationActivityMode() {
return CompilationActivityMode.RUN_COMPILATION;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -40,6 +40,9 @@
*/
package com.oracle.truffle.runtime.hotspot;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.ref.Reference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
Expand Down Expand Up @@ -692,4 +695,45 @@ public boolean isLibGraalCompilationEnabled() {
return compilationSupport instanceof LibGraalTruffleCompilationSupport;
}

private static final MethodHandle getCompilationActivityMode;
static {
MethodHandle mHandle = null;
try {
MethodType mt = MethodType.methodType(int.class);
mHandle = MethodHandles.lookup().findVirtual(HotSpotJVMCIRuntime.class, "getCompilationActivityMode", mt);
} catch (NoSuchMethodException | IllegalAccessException e) {
// Older JVMCI runtimes might not support `getCompilationActivityMode()`
}
getCompilationActivityMode = mHandle;
}

/**
* Returns the current host compilation activity mode based on HotSpot's code cache state.
*/
@Override
protected CompilationActivityMode getCompilationActivityMode() {
int activityMode = 1; // Default is to run compilations
if (getCompilationActivityMode != null) {
try {
activityMode = (int) getCompilationActivityMode.invokeExact(HotSpotJVMCIRuntime.runtime());
} catch (Throwable t) {
throw CompilerDirectives.shouldNotReachHere("Can't get HotSpot's compilation activity mode", t);
}
}
return resolveHotSpotActivityMode(activityMode);
}

/**
* Represents HotSpot's compilation activity mode which is one of: {@code stop_compilation = 0},
* {@code run_compilation = 1} or {@code shutdown_compilation = 2}. Should be in sync with the
* {@code CompilerActivity} enum in {@code hotspot/share/compiler/compileBroker.hpp}.
*/
private static CompilationActivityMode resolveHotSpotActivityMode(int i) {
return switch (i) {
case 0 -> CompilationActivityMode.STOP_COMPILATION;
case 1 -> CompilationActivityMode.RUN_COMPILATION;
case 2 -> CompilationActivityMode.SHUTDOWN_COMPILATION;
default -> throw CompilerDirectives.shouldNotReachHere("Invalid CompilationActivityMode " + i);
};
}
}

0 comments on commit dd2c3a2

Please sign in to comment.