Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Truffle compiler control based on HotSpot's CompileBroker compilation activity #10135

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -81,6 +84,7 @@
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.compiler.TruffleCompilable;
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 @@ -855,6 +859,62 @@ final boolean isCompilationFailed() {
return compilationFailed;
}

final CompilationActivityMode getCompilationActivityMode() {
CompilationActivityMode compilationActivityMode = runtime().getCompilationActivityMode();
long sct = runtime().stoppedCompilationTime().get();
if (compilationActivityMode == CompilationActivityMode.STOP_COMPILATION) {
if (sct != 0 && System.currentTimeMillis() - sct > engine.stoppedCompilationRetryDelay) {
runtime().stoppedCompilationTime().compareAndSet(sct, 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 (sct == 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) {
simonis marked this conversation as resolved.
Show resolved Hide resolved
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:
CompilerDirectives.shouldNotReachHere("Invalid compilation activity mode: " + compilationActivityMode);
}
return CompilationActivityMode.RUN_COMPILATION;
}

/**
* 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 @@ -866,13 +926,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,30 @@ static OptionCategory matchCategory(TruffleCompilerOptionDescriptor d) {
}
}

/**
* 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}.
*/
public enum CompilationActivityMode {
STOP_COMPILATION,
RUN_COMPILATION,
SHUTDOWN_COMPILATION;

public static CompilationActivityMode fromInteger(int i) {
return switch (i) {
case 0 -> STOP_COMPILATION;
case 1 -> RUN_COMPILATION;
case 2 -> SHUTDOWN_COMPILATION;
default -> throw new RuntimeException("Invalid CompilationActivityMode " + i);
};
}
}

/**
* Returns the current host compilation activity mode. The default is to run compilations.
*/
public 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,31 @@ public boolean isLibGraalCompilationEnabled() {
return compilationSupport instanceof LibGraalTruffleCompilationSupport;
}

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()`
simonis marked this conversation as resolved.
Show resolved Hide resolved
}
getCompilationActivityMode = mHandle;
}

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