Skip to content

Commit 1949dde

Browse files
chumermur47x111
authored andcommitted
Implement threaded-switch for the bytecode DSL; Add more bytecode interpreter benchmarks for threaded-switch.
1 parent e3b8a47 commit 1949dde

File tree

9 files changed

+348
-5
lines changed

9 files changed

+348
-5
lines changed

truffle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
2424
* GR-8251: `DebuggerSession.resumeThread(Thread)` no longer cancels ongoing step operations. Stepping is now independent of other debugger actions to enhance flexibility.
2525
* GR-61293: Bytecode DSL: Specialization state is now inlined into the bytecode array, which reduces memory footprint and interpreter execution time.
2626
* GR-68993: Added `HostCompilerDirectives.markThreadedSwitch(int)` to mark a switch statement within a loop as a candidate for threaded switch optimization.
27+
* GR-68993: Bytecode DSL: All bytecode interpreters are now using the threaded switch optimization by default. This new optimization can be configured using `@GenerateBytecode(enableThreadedSwitch=true|false)`.
2728

2829
## Version 25.0
2930
* GR-31495 Added ability to specify language and instrument specific options using `Source.Builder.option(String, String)`. Languages may describe available source options by implementing `TruffleLanguage.getSourceOptionDescriptors()` and `TruffleInstrument.getSourceOptionDescriptors()` respectively.

truffle/src/com.oracle.truffle.api.bytecode/snapshot.sigtest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ meth public abstract !hasdefault boolean enableRootTagging()
315315
meth public abstract !hasdefault boolean enableSerialization()
316316
meth public abstract !hasdefault boolean enableSpecializationIntrospection()
317317
meth public abstract !hasdefault boolean enableTagInstrumentation()
318+
meth public abstract !hasdefault boolean enableThreadedSwitch()
318319
meth public abstract !hasdefault boolean enableUncachedInterpreter()
319320
meth public abstract !hasdefault boolean enableYield()
320321
meth public abstract !hasdefault boolean storeBytecodeIndexInFrame()

truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/GenerateBytecode.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import java.lang.annotation.Target;
4747

4848
import com.oracle.truffle.api.CompilerAsserts;
49+
import com.oracle.truffle.api.HostCompilerDirectives;
4950
import com.oracle.truffle.api.TruffleLanguage;
5051
import com.oracle.truffle.api.bytecode.debug.BytecodeDebugListener;
5152
import com.oracle.truffle.api.frame.Frame;
@@ -542,4 +543,23 @@
542543
*/
543544
boolean additionalAssertions() default false;
544545

546+
/**
547+
* Enables the use of the {@link HostCompilerDirectives#markThreadedSwitch(int) threaded switch}
548+
* for the generated bytecode switch.
549+
* <p>
550+
* The threaded-switch mechanism reduces dispatch overhead for large or frequently executed
551+
* bytecode switches, improving interpreter performance. Disabling it forces a traditional
552+
* (non-threaded) switch implementation, which can be useful for benchmarking or debugging but
553+
* is generally slower.
554+
* <p>
555+
* This option is enabled by default. It is not recommended to disable the threaded switch in
556+
* production, as doing so may significantly reduce performance without improving stability. On
557+
* JVMs that do not support {@link HostCompilerDirectives#markThreadedSwitch(int)} this flag has
558+
* no effect.
559+
*
560+
* @see HostCompilerDirectives#markThreadedSwitch(int)
561+
* @since 26.0
562+
*/
563+
boolean enableThreadedSwitch() default true;
564+
545565
}

truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeRootNodeElement.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14511,7 +14511,17 @@ private List<CodeExecutableElement> createContinueAt() {
1451114511
}
1451214512

1451314513
b.startTryBlock();
14514-
b.startSwitch().tree(op).end().startBlock();
14514+
b.startSwitch();
14515+
14516+
if (model.enableThreadedSwitch) {
14517+
b.startStaticCall(types.HostCompilerDirectives, "markThreadedSwitch");
14518+
}
14519+
b.tree(op);
14520+
if (model.enableThreadedSwitch) {
14521+
b.end();
14522+
}
14523+
14524+
b.end().startBlock();
1451514525

1451614526
List<InstructionModel> topLevelInstructions = instructionPartitions.get(0);
1451714527
Map<Boolean, List<InstructionModel>> groupedInstructions = topLevelInstructions.stream().collect(deterministicGroupingBy((i) -> isForceCached(tier, i)));

truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLModel.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ public BytecodeDSLModel(ProcessorContext context, TypeElement templateType, Anno
130130
public boolean enableRootTagging;
131131
public boolean enableRootBodyTagging;
132132
public boolean enableBlockScoping;
133+
public boolean enableThreadedSwitch;
133134
public String defaultLocalValue;
134135
public DSLExpression defaultLocalValueExpression;
135136
public String variadicStackLimit;

truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/parser/BytecodeDSLParser.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ private void parseBytecodeDSLModel(TypeElement typeElement, BytecodeDSLModel mod
248248
model.bytecodeDebugListener = (!enableBytecodeDebugListener || types.BytecodeDebugListener == null) ? false : ElementUtils.isAssignable(typeElement.asType(), types.BytecodeDebugListener);
249249
model.additionalAssertions = TruffleProcessorOptions.additionalAssertions(processingEnv) ||
250250
ElementUtils.getAnnotationValue(Boolean.class, generateBytecodeMirror, "additionalAssertions", true);
251+
model.enableThreadedSwitch = ElementUtils.getAnnotationValue(Boolean.class, generateBytecodeMirror, "enableThreadedSwitch");
251252

252253
BytecodeDSLBuiltins.addBuiltins(model, types, context);
253254

truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/bytecode_dsl/BytecodeDSLBenchmarkRootNode.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,16 @@
5252
import com.oracle.truffle.api.nodes.RootNode;
5353

5454
@GenerateBytecodeTestVariants({
55-
@Variant(suffix = "NoOpts", configuration = @GenerateBytecode(languageClass = BenchmarkLanguage.class, enableTagInstrumentation = true, enableYield = true)),
56-
@Variant(suffix = "Uncached", configuration = @GenerateBytecode(languageClass = BenchmarkLanguage.class, enableTagInstrumentation = true, enableYield = true, enableUncachedInterpreter = true)),
57-
@Variant(suffix = "AllOpts", configuration = @GenerateBytecode(languageClass = BenchmarkLanguage.class, enableTagInstrumentation = true, enableYield = true, enableUncachedInterpreter = true, boxingEliminationTypes = {
58-
int.class, boolean.class}))
55+
@Variant(suffix = "NoOpts", configuration = @GenerateBytecode(languageClass = BenchmarkLanguage.class, //
56+
enableThreadedSwitch = false, enableTagInstrumentation = true, enableYield = true)),
57+
@Variant(suffix = "Uncached", configuration = @GenerateBytecode(languageClass = BenchmarkLanguage.class, //
58+
enableThreadedSwitch = true, enableTagInstrumentation = true, enableYield = true, enableUncachedInterpreter = true)),
59+
@Variant(suffix = "AllOpts", configuration = @GenerateBytecode(languageClass = BenchmarkLanguage.class, //
60+
enableThreadedSwitch = false, enableTagInstrumentation = true, enableYield = true, enableUncachedInterpreter = true, boxingEliminationTypes = {
61+
int.class, boolean.class})),
62+
@Variant(suffix = "ThreadedAllOpts", configuration = @GenerateBytecode(languageClass = BenchmarkLanguage.class, //
63+
enableThreadedSwitch = true, enableTagInstrumentation = true, enableYield = true, enableUncachedInterpreter = true, boxingEliminationTypes = {
64+
int.class, boolean.class}))
5965
})
6066
public abstract class BytecodeDSLBenchmarkRootNode extends RootNode implements BytecodeRootNode {
6167

truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/bytecode_dsl/SimpleBytecodeBenchmark.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.graalvm.truffle.benchmark.bytecode_dsl.manual.Builder;
4848
import org.graalvm.truffle.benchmark.bytecode_dsl.manual.BytecodeInterpreterAllOpts;
4949
import org.graalvm.truffle.benchmark.bytecode_dsl.manual.BytecodeInterpreterNoOpts;
50+
import org.graalvm.truffle.benchmark.bytecode_dsl.manual.BytecodeInterpreterThreadedAllOpts;
5051
import org.graalvm.truffle.benchmark.bytecode_dsl.specs.BenchmarkSpec;
5152
import org.openjdk.jmh.annotations.Benchmark;
5253
import org.openjdk.jmh.annotations.Level;
@@ -88,6 +89,7 @@ public void setup(BenchmarkParams params) {
8889
callTarget = switch (benchMethod) {
8990
case "bytecodeDSLNoOpts" -> createBytecodeDSLNodes(BytecodeDSLBenchmarkRootNodeNoOpts.class, null, benchmarkSpec::parseBytecodeDSL).getNodes().getLast().getCallTarget();
9091
case "bytecodeDSLAllOpts" -> createBytecodeDSLNodes(BytecodeDSLBenchmarkRootNodeAllOpts.class, null, benchmarkSpec::parseBytecodeDSL).getNodes().getLast().getCallTarget();
92+
case "bytecodeDSLThreadedAllOpts" -> createBytecodeDSLNodes(BytecodeDSLBenchmarkRootNodeThreadedAllOpts.class, null, benchmarkSpec::parseBytecodeDSL).getNodes().getLast().getCallTarget();
9193
case "bytecodeDSLUncached" -> {
9294
var node = createBytecodeDSLNodes(BytecodeDSLBenchmarkRootNodeUncached.class, null, benchmarkSpec::parseBytecodeDSL).getNodes().getLast();
9395
node.getBytecodeNode().setUncachedThreshold(Integer.MIN_VALUE);
@@ -103,6 +105,11 @@ public void setup(BenchmarkParams params) {
103105
benchmarkSpec.parseBytecode(builder);
104106
yield createBytecodeNodes(BytecodeInterpreterAllOpts.class, null, builder).getCallTarget();
105107
}
108+
case "manualThreadedAllOpts" -> {
109+
var builder = Builder.newBuilder();
110+
benchmarkSpec.parseBytecode(builder);
111+
yield createBytecodeNodes(BytecodeInterpreterThreadedAllOpts.class, null, builder).getCallTarget();
112+
}
106113
case "ast" -> benchmarkSpec.parseAST(null);
107114
default -> throw new AssertionError("Unexpected benchmark method " + benchMethod);
108115
};
@@ -138,6 +145,11 @@ public void bytecodeDSLUncached() {
138145
benchmark(callTarget);
139146
}
140147

148+
@Benchmark
149+
public void bytecodeDSLThreadedAllOpts() {
150+
benchmark(callTarget);
151+
}
152+
141153
@Benchmark
142154
public void manualNoOpts() {
143155
benchmark(callTarget);
@@ -148,6 +160,11 @@ public void manualAllOpts() {
148160
benchmark(callTarget);
149161
}
150162

163+
@Benchmark
164+
public void manualThreadedAllOpts() {
165+
benchmark(callTarget);
166+
}
167+
151168
@Benchmark
152169
public void ast() {
153170
benchmark(callTarget);

0 commit comments

Comments
 (0)