Skip to content

Commit c839309

Browse files
authored
Speedup DataflowError.withDefaultTrace (#11153)
Improves the speed of `ExecutionEnvironment.hasContextEnabled`. # Important Notes Local speedup of `Map_Error_Benchmark_Vector_ignore.Map_Id_All_Errors` benchmark is roughly ???.
1 parent 3390491 commit c839309

File tree

8 files changed

+157
-81
lines changed

8 files changed

+157
-81
lines changed

engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/ThrowErrorNode.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.oracle.truffle.api.nodes.Node;
55
import org.enso.interpreter.dsl.BuiltinMethod;
66
import org.enso.interpreter.runtime.error.DataflowError;
7+
import org.enso.interpreter.runtime.state.HasContextEnabledNode;
78
import org.enso.interpreter.runtime.state.State;
89

910
@BuiltinMethod(
@@ -12,7 +13,9 @@
1213
description = "Returns a new value error with given payload.",
1314
inlineable = true)
1415
public class ThrowErrorNode extends Node {
16+
private @Child HasContextEnabledNode hasContextEnabledNode = HasContextEnabledNode.create();
17+
1518
public Object execute(VirtualFrame giveMeAStackFrame, State state, Object payload) {
16-
return DataflowError.withDefaultTrace(state, payload, this);
19+
return DataflowError.withDefaultTrace(state, payload, this, hasContextEnabledNode);
1720
}
1821
}

engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/ContextIsEnabledNode.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
import org.enso.interpreter.runtime.data.atom.Atom;
88
import org.enso.interpreter.runtime.error.PanicException;
99
import org.enso.interpreter.runtime.state.ExecutionEnvironment;
10+
import org.enso.interpreter.runtime.state.HasContextEnabledNode;
1011

1112
@BuiltinMethod(
1213
type = "Context",
1314
name = "is_enabled_builtin",
1415
description = "Check if the context is enabled in the provided execution environment.")
1516
public class ContextIsEnabledNode extends Node {
1617
private @Child ExpectStringNode expectStringNode = ExpectStringNode.build();
18+
private @Child HasContextEnabledNode hasContextEnabledNode = HasContextEnabledNode.create();
1719

1820
Object execute(Atom self, Object environmentName) {
1921
String envName = expectStringNode.execute(environmentName);
@@ -26,6 +28,6 @@ Object execute(Atom self, Object environmentName) {
2628
.makeUnimplemented("execution environment mismatch");
2729
throw new PanicException(error, this);
2830
}
29-
return currentEnv.hasContextEnabled(self.getConstructor().getName());
31+
return hasContextEnabledNode.executeHasContextEnabled(currentEnv, self.getConstructor());
3032
}
3133
}

engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.enso.interpreter.runtime.scope.TopLevelScope;
6060
import org.enso.interpreter.runtime.state.ExecutionEnvironment;
6161
import org.enso.interpreter.runtime.state.State;
62+
import org.enso.interpreter.runtime.state.WithContextNode;
6263
import org.enso.interpreter.runtime.util.TruffleFileSystem;
6364
import org.enso.librarymanager.ProjectLoadingFailure;
6465
import org.enso.librarymanager.resolved.LibraryRoot;
@@ -894,7 +895,9 @@ public void setExecutionEnvironment(ExecutionEnvironment executionEnvironment) {
894895
public ExecutionEnvironment enableExecutionEnvironment(Atom context, String environmentName) {
895896
ExecutionEnvironment original = globalExecutionEnvironment;
896897
if (original.getName().equals(environmentName)) {
897-
setExecutionEnvironment(original.withContextEnabled(context));
898+
var newExecEnv =
899+
WithContextNode.getUncached().executeEnvironmentUpdate(original, context, true);
900+
setExecutionEnvironment(newExecEnv);
898901
}
899902
return original;
900903
}
@@ -909,7 +912,9 @@ public ExecutionEnvironment enableExecutionEnvironment(Atom context, String envi
909912
public ExecutionEnvironment disableExecutionEnvironment(Atom context, String environmentName) {
910913
ExecutionEnvironment original = globalExecutionEnvironment;
911914
if (original.getName().equals(environmentName)) {
912-
setExecutionEnvironment(original.withContextDisabled(context));
915+
var newExecEnv =
916+
WithContextNode.getUncached().executeEnvironmentUpdate(original, context, false);
917+
setExecutionEnvironment(newExecEnv);
913918
}
914919
return original;
915920
}

engine/runtime/src/main/java/org/enso/interpreter/runtime/error/DataflowError.java

+11-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.enso.interpreter.runtime.data.Type;
2525
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
2626
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
27+
import org.enso.interpreter.runtime.state.HasContextEnabledNode;
2728
import org.enso.interpreter.runtime.state.State;
2829

2930
/**
@@ -51,15 +52,18 @@ public final class DataflowError extends AbstractTruffleException implements Ens
5152
* @param location the node in which the error was created
5253
* @return a new dataflow error
5354
*/
54-
public static DataflowError withDefaultTrace(State state, Object payload, Node location) {
55+
public static DataflowError withDefaultTrace(
56+
State state, Object payload, Node location, HasContextEnabledNode hasContextEnabledNode) {
5557
assert payload != null;
58+
var ensoCtx = EnsoContext.get(location);
59+
var dataflowStacktraceCtx = ensoCtx.getBuiltins().context().getDataflowStackTrace();
5660
boolean attachFullStackTrace =
5761
state == null
58-
|| EnsoContext.get(location)
59-
.getExecutionEnvironment()
60-
.hasContextEnabled("Dataflow_Stack_Trace");
62+
|| hasContextEnabledNode.executeHasContextEnabled(
63+
ensoCtx.getExecutionEnvironment(), dataflowStacktraceCtx);
6164
if (attachFullStackTrace) {
62-
var result = new DataflowError(payload, UNLIMITED_STACK_TRACE, location);
65+
var result =
66+
new DataflowError(payload, AbstractTruffleException.UNLIMITED_STACK_TRACE, location);
6367
TruffleStackTrace.fillIn(result);
6468
return result;
6569
} else {
@@ -68,8 +72,9 @@ public static DataflowError withDefaultTrace(State state, Object payload, Node l
6872
}
6973
}
7074

75+
/** Slow version of {@link #withDefaultTrace(State, Object, Node, HasContextEnabledNode)}. */
7176
public static DataflowError withDefaultTrace(Object payload, Node location) {
72-
return withDefaultTrace(null, payload, location);
77+
return withDefaultTrace(null, payload, location, HasContextEnabledNode.getUncached());
7378
}
7479

7580
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.enso.interpreter.runtime.state;
2+
3+
/** Fields correspond to the constructors of {@code Standard.Base.Runtime.Context} builtin type. */
4+
record ContextPermissions(boolean input, boolean output, boolean dataflowStacktrace) {}

engine/runtime/src/main/java/org/enso/interpreter/runtime/state/ExecutionEnvironment.java

+6-71
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
package org.enso.interpreter.runtime.state;
22

3-
import org.enso.interpreter.node.expression.builtin.runtime.Context;
4-
import org.enso.interpreter.runtime.EnsoContext;
5-
import org.enso.interpreter.runtime.data.atom.Atom;
6-
73
public class ExecutionEnvironment {
84

95
private final String name;
106

11-
// Ideally we would "just" use a map here. But that leads
12-
// to native image build problems. This in turn leads to
13-
// TruffleBoundary annotations which in turn leads to slow path.
14-
private final String[] keys;
15-
private final Boolean[] permissions;
7+
final ContextPermissions permissions;
168

179
public static final String LIVE_ENVIRONMENT_NAME = "live";
1810

@@ -22,82 +14,25 @@ public class ExecutionEnvironment {
2214
public static final ExecutionEnvironment DESIGN =
2315
new ExecutionEnvironment(DESIGN_ENVIRONMENT_NAME);
2416

25-
private static final ExecutionEnvironment initLive(String name) {
26-
String[] keys = new String[] {Context.INPUT_NAME, Context.OUTPUT_NAME};
27-
Boolean[] permissions = new Boolean[] {true, true};
28-
return new ExecutionEnvironment(name, keys, permissions);
17+
private static ExecutionEnvironment initLive(String name) {
18+
var permissions = new ContextPermissions(true, true, false);
19+
return new ExecutionEnvironment(name, permissions);
2920
}
3021

3122
public ExecutionEnvironment(String name) {
3223
this.name = name;
33-
this.keys = new String[0];
34-
this.permissions = new Boolean[0];
24+
this.permissions = new ContextPermissions(false, false, false);
3525
}
3626

37-
private ExecutionEnvironment(String name, String[] keys, Boolean[] permissions) {
27+
ExecutionEnvironment(String name, ContextPermissions permissions) {
3828
this.name = name;
39-
this.keys = keys;
4029
this.permissions = permissions;
4130
}
4231

4332
public String getName() {
4433
return this.name;
4534
}
4635

47-
public ExecutionEnvironment withContextEnabled(Atom context) {
48-
return update(context, true);
49-
}
50-
51-
public ExecutionEnvironment withContextDisabled(Atom context) {
52-
return update(context, false);
53-
}
54-
55-
private ExecutionEnvironment update(Atom context, boolean value) {
56-
assert context.getConstructor().getType()
57-
== EnsoContext.get(null).getBuiltins().context().getType();
58-
int keyFound = -1;
59-
for (int i = 0; i < keys.length; i++) {
60-
if (keys[i].equals(context.getConstructor().getName())) {
61-
keyFound = i;
62-
}
63-
}
64-
String[] keys1;
65-
Boolean[] permissions1;
66-
if (keyFound != -1) {
67-
keys1 = cloneArray(keys, new String[keys.length]);
68-
permissions1 = cloneArray(permissions, new Boolean[keys.length]);
69-
permissions1[keyFound] = value;
70-
} else {
71-
keys1 = cloneArray(keys, new String[keys.length + 1]);
72-
permissions1 = cloneArray(permissions, new Boolean[keys.length + 1]);
73-
keyFound = keys.length;
74-
keys1[keyFound] = context.getConstructor().getName();
75-
permissions1[keyFound] = value;
76-
}
77-
return new ExecutionEnvironment(name, keys1, permissions1);
78-
}
79-
80-
private <T> T[] cloneArray(T[] fromArray, T[] toArray) {
81-
for (int i = 0; i < fromArray.length; i++) {
82-
toArray[i] = fromArray[i];
83-
}
84-
return toArray;
85-
}
86-
87-
public Boolean hasContextEnabled(String context) {
88-
int keyFound = -1;
89-
for (int i = 0; i < keys.length; i++) {
90-
if (keys[i].equals(context)) {
91-
keyFound = i;
92-
}
93-
}
94-
if (keyFound != -1) {
95-
return permissions[keyFound];
96-
} else {
97-
return false;
98-
}
99-
}
100-
10136
public static ExecutionEnvironment forName(String name) {
10237
switch (name) {
10338
case LIVE_ENVIRONMENT_NAME:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package org.enso.interpreter.runtime.state;
2+
3+
import com.oracle.truffle.api.CompilerDirectives;
4+
import com.oracle.truffle.api.dsl.Cached;
5+
import com.oracle.truffle.api.dsl.GenerateUncached;
6+
import com.oracle.truffle.api.dsl.Specialization;
7+
import com.oracle.truffle.api.nodes.Node;
8+
import org.enso.interpreter.runtime.EnsoContext;
9+
import org.enso.interpreter.runtime.data.atom.AtomConstructor;
10+
11+
/**
12+
* A node representing the functionality done by {@code Standard.Base.Runtime.Context.is_enabled}.
13+
*/
14+
@GenerateUncached
15+
public abstract class HasContextEnabledNode extends Node {
16+
17+
public static HasContextEnabledNode getUncached() {
18+
return HasContextEnabledNodeGen.getUncached();
19+
}
20+
21+
public static HasContextEnabledNode create() {
22+
return HasContextEnabledNodeGen.create();
23+
}
24+
25+
/**
26+
* Returns true if the context represented by the given {@code runtimeCtxCtor} is enabled in the
27+
* given {@code executionEnvironment}.
28+
*
29+
* @param runtimeCtxCtor Constructor of {@code Standard.Base.Runtime.Context}.
30+
*/
31+
public abstract boolean executeHasContextEnabled(
32+
ExecutionEnvironment executionEnvironment, AtomConstructor runtimeCtxCtor);
33+
34+
@Specialization(guards = "executionEnvironment == cachedEnv", limit = "3")
35+
boolean cachedHasContextEnabled(
36+
ExecutionEnvironment executionEnvironment,
37+
AtomConstructor runtimeCtxCtor,
38+
@Cached("executionEnvironment") ExecutionEnvironment cachedEnv) {
39+
return doIt(cachedEnv, runtimeCtxCtor);
40+
}
41+
42+
@Specialization(replaces = "cachedHasContextEnabled")
43+
boolean uncachedHasContextEnabled(
44+
ExecutionEnvironment executionEnvironment, AtomConstructor runtimeCtxCtor) {
45+
return doIt(executionEnvironment, runtimeCtxCtor);
46+
}
47+
48+
private boolean doIt(ExecutionEnvironment executionEnvironment, AtomConstructor runtimeCtxCtor) {
49+
var ensoCtx = EnsoContext.get(this);
50+
var contextBuiltin = ensoCtx.getBuiltins().context();
51+
if (runtimeCtxCtor == contextBuiltin.getInput()) {
52+
return executionEnvironment.permissions.input();
53+
} else if (runtimeCtxCtor == contextBuiltin.getOutput()) {
54+
return executionEnvironment.permissions.output();
55+
} else if (runtimeCtxCtor == contextBuiltin.getDataflowStackTrace()) {
56+
return executionEnvironment.permissions.dataflowStacktrace();
57+
} else {
58+
CompilerDirectives.transferToInterpreter();
59+
throw ensoCtx.raiseAssertionPanic(this, "Unknown context: " + runtimeCtxCtor, null);
60+
}
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.enso.interpreter.runtime.state;
2+
3+
import com.oracle.truffle.api.dsl.GenerateUncached;
4+
import com.oracle.truffle.api.dsl.Specialization;
5+
import com.oracle.truffle.api.nodes.Node;
6+
import org.enso.interpreter.runtime.EnsoContext;
7+
import org.enso.interpreter.runtime.data.atom.Atom;
8+
9+
/**
10+
* A node representing functionality done by {@code Standard.Base.Runtime.Context.with_enabled} and
11+
* {@code Standard.Base.Runtime.Context.with_disabled}. That is, it enables or disables the given
12+
* context in the current {@link ExecutionEnvironment execution environment}.
13+
*/
14+
@GenerateUncached
15+
public abstract class WithContextNode extends Node {
16+
public static WithContextNode getUncached() {
17+
return WithContextNodeGen.getUncached();
18+
}
19+
20+
public static WithContextNode create() {
21+
return WithContextNodeGen.create();
22+
}
23+
24+
/**
25+
* Returns a new {@link ExecutionEnvironment} with the given context enabled or disabled.
26+
*
27+
* @param current Current execution environment.
28+
* @param context Atom of type {@code Standard.Base.Runtime.Context}.
29+
* @param enabled Whether to enable or disable the context.
30+
*/
31+
public abstract ExecutionEnvironment executeEnvironmentUpdate(
32+
ExecutionEnvironment current, Atom context, boolean enabled);
33+
34+
@Specialization
35+
ExecutionEnvironment doIt(ExecutionEnvironment current, Atom context, boolean enabled) {
36+
var ensoCtx = EnsoContext.get(this);
37+
var contextBuiltin = ensoCtx.getBuiltins().context();
38+
if (context.getConstructor().getType() != contextBuiltin.getType()) {
39+
throw ensoCtx.raiseAssertionPanic(this, "Invalid context type", null);
40+
}
41+
var ctor = context.getConstructor();
42+
ContextPermissions newPermissions;
43+
if (ctor == contextBuiltin.getInput()) {
44+
newPermissions =
45+
new ContextPermissions(
46+
enabled, current.permissions.output(), current.permissions.dataflowStacktrace());
47+
} else if (ctor == contextBuiltin.getOutput()) {
48+
newPermissions =
49+
new ContextPermissions(
50+
current.permissions.input(), enabled, current.permissions.dataflowStacktrace());
51+
} else if (ctor == contextBuiltin.getDataflowStackTrace()) {
52+
newPermissions =
53+
new ContextPermissions(
54+
current.permissions.input(), current.permissions.output(), enabled);
55+
} else {
56+
throw ensoCtx.raiseAssertionPanic(this, "Unknown context: " + ctor, null);
57+
}
58+
return new ExecutionEnvironment(current.getName(), newPermissions);
59+
}
60+
}

0 commit comments

Comments
 (0)