Skip to content

Commit 471468b

Browse files
committed
[GR-6155] [GR-6358] Faster polyglot calls; fix host language multithreading; reenable threads closed assertion for languages.
PullRequest: graal/596
2 parents 943498b + 206f8b0 commit 471468b

File tree

21 files changed

+941
-247
lines changed

21 files changed

+941
-247
lines changed

compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTVMCI.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,28 @@ protected boolean isGuestCallStackFrame(StackTraceElement e) {
5959
return e.getMethodName().equals(OptimizedCallTarget.CALL_BOUNDARY_METHOD_NAME) && e.getClassName().equals(OptimizedCallTarget.class.getName());
6060
}
6161

62+
/**
63+
* Initializes the argument profile with a custom profile without calling it. A call target must
64+
* never be called prior initialization of argument types. Also the argument types must be final
65+
* if used in combination with {@link #callProfiled(CallTarget, Object...)}.
66+
*/
67+
@Override
68+
protected void initializeProfile(CallTarget target, Class<?>[] argumentTypes) {
69+
((OptimizedCallTarget) target).getCompilationProfile().initializeArgumentTypes(argumentTypes);
70+
}
71+
72+
/**
73+
* Call without verifying the argument profile. Needs to be initialized by
74+
* {@link #initializeProfile(CallTarget, Class[])}. Potentially crashes the VM if the argument
75+
* profile is incompatible with the actual arguments. Use with caution.
76+
*/
77+
@Override
78+
protected Object callProfiled(CallTarget target, Object... args) {
79+
OptimizedCallTarget castTarget = (OptimizedCallTarget) target;
80+
assert castTarget.compilationProfile != null && castTarget.compilationProfile.isValidArgumentProfile(args) : "Invalid argument profile. UnsafeCalls need to explicity initialize the profile.";
81+
return castTarget.doInvoke(args);
82+
}
83+
6284
@Override
6385
protected OptionDescriptors getCompilerOptionDescriptors() {
6486
return PolyglotCompilerOptions.getDescriptors();

compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCompilationProfile.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ public String toString() {
8282
compilationCallAndLoopThreshold);
8383
}
8484

85+
void initializeArgumentTypes(Class<?>[] argumentTypes) {
86+
CompilerAsserts.neverPartOfCompilation();
87+
if (profiledArgumentTypesAssumption != null) {
88+
this.profiledArgumentTypesAssumption.invalidate();
89+
throw new AssertionError("Argument types already initialized. initializeArgumentTypes must be called before any profile is initialized.");
90+
} else {
91+
this.profiledArgumentTypesAssumption = createAssumption("Custom profiled argument types");
92+
this.profiledArgumentTypes = argumentTypes;
93+
}
94+
}
95+
8596
Class<?>[] getProfiledArgumentTypes() {
8697
if (profiledArgumentTypesAssumption == null) {
8798
/*
@@ -315,6 +326,18 @@ private void updateProfiledArgumentTypes(Object[] args, Class<?>[] types) {
315326
profiledArgumentTypesAssumption = createAssumption("Profiled Argument Types");
316327
}
317328

329+
private static boolean checkProfiledArgumentTypes(Object[] args, Class<?>[] types) {
330+
assert types != null;
331+
if (args.length != types.length) {
332+
throw new ArrayIndexOutOfBoundsException();
333+
}
334+
for (int j = 0; j < types.length; j++) {
335+
// throws ClassCast on error
336+
types[j].cast(args[j]);
337+
}
338+
return true;
339+
}
340+
318341
private static Class<?> classOf(Object arg) {
319342
return arg != null ? arg.getClass() : null;
320343
}
@@ -385,4 +408,8 @@ public static OptimizedCompilationProfile create(OptionValues options) {
385408
private static OptimizedAssumption createAssumption(String name) {
386409
return (OptimizedAssumption) Truffle.getRuntime().createAssumption(name);
387410
}
411+
412+
public boolean isValidArgumentProfile(Object[] args) {
413+
return profiledArgumentTypesAssumption != null && profiledArgumentTypesAssumption.isValid() && checkProfiledArgumentTypes(args, profiledArgumentTypes);
414+
}
388415
}

sdk/CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Graal SDK Changelog
2+
3+
This changelog summarizes major changes between Graal SDK versions. The main focus is on APIs exported by Graal SDK.
4+
5+
## Version 0.29
6+
7+
* Introduced Context.enter() and Context.leave() that allows explicitly entering and leaving the context to improve performance of performing many simple operations.
8+
* Introduced Value.executeVoid to allow execution of functions more efficiently if not return value is expected.
9+
10+
11+
## Version 0.26
12+
13+
* Initial revision of the polyglot API introduced.
14+
* Initial revision of the native image API introduced.
15+
* Initial revision of the options API introduced.

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Context.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,37 @@ public boolean initialize(String languageId) {
255255
return impl.initializeLanguage(languageId);
256256
}
257257

258+
/**
259+
* Explicitly enters this context on the current thread. A context needs to be entered for any
260+
* operation to be performed. The context implicitly enters and leaves the context for every
261+
* operation. For example, before and after invoking the {@link Value#execute(Object...)
262+
* execute} method. This can be inefficient if a very high number of simple operations needs to
263+
* be performed. By {@link #enter() entering} and {@link #leave() leaving} once explicitly, the
264+
* overhead for entering/leaving contexts for each operation can be eliminated. Contexts can be
265+
* entered multiple times on the same thread.
266+
*
267+
* @throws IllegalStateException if the context is already {@link #close() closed}.
268+
* @throws PolyglotException if a language has denied execution on the current thread.
269+
* @see #leave() to leave a context.
270+
* @since 1.0
271+
*/
272+
public void enter() {
273+
impl.explicitEnter();
274+
}
275+
276+
/**
277+
* Explicitly leaves this context on the current thread. The context must be {@link #enter()
278+
* entered} before calling this method.
279+
*
280+
* @throws IllegalStateException if the context is already closed or if the context was not
281+
* {@link #enter() entered} on the current thread.
282+
* @see #enter() to enter a context.
283+
* @since 1.0
284+
*/
285+
public void leave() {
286+
impl.explicitLeave();
287+
}
288+
258289
/**
259290
* Closes this context and frees up potentially allocated native resources. A context cannot
260291
* free all native resources allocated automatically. For this reason it is necessary to close

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@
2929
import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractValueImpl;
3030

3131
/**
32-
* Represents a polyglot value. Polyglot values can either result from a {@link #isHostObject()
33-
* host} or guest language. Polyglot values are bound to a {@link Context context}.
32+
* Represents a polyglot value that can be accessed using a set of language agnostic operations.
33+
* Polyglot values can either result from a {@link #isHostObject() host} or guest language. Polyglot
34+
* values are bound to a {@link Context context}. If the context is closed then the value operation
35+
* throw an {@link IllegalStateException}.
3436
*
3537
* @since 1.0
3638
*/
@@ -144,22 +146,35 @@ public void putMember(String key, Object member) {
144146
// executable
145147

146148
/**
147-
* Returns <code>true</code> if the value can be executed. This indicates that the
148-
* {@link #execute(Object...)} can be used with this value.
149+
* Returns <code>true</code> if the value can be {@link #execute(Object...) executed}.
149150
*
151+
* @throws IllegalStateException if the underlying context was closed
152+
* @see #execute(Object...)
150153
* @since 1.0
151154
*/
152155
public boolean canExecute() {
153156
return impl.canExecute(receiver);
154157
}
155158

156159
/**
160+
* Executes this value if it {@link #canExecute() can} be executed and returns its result. If no
161+
* result value is expected or needed use {@link #executeVoid(Object...)} for better
162+
* performance.
157163
*
164+
* @throws IllegalStateException if the underlying context was closed
165+
* @throws UnsupportedOperationException if this object cannot be executed.
166+
* @throws PolyglotException if a guest language error occurred during execution.
167+
* @see #executeVoid(Object...)
158168
*
159169
* @since 1.0
160170
*/
161171
public Value execute(Object... arguments) {
162-
return impl.execute(receiver, arguments);
172+
if (arguments.length == 0) {
173+
// specialized entry point for zero argument execute calls
174+
return impl.execute(receiver);
175+
} else {
176+
return impl.execute(receiver, arguments);
177+
}
163178
}
164179

165180
/**
@@ -181,6 +196,26 @@ public Value newInstance(Object... arguments) {
181196
return impl.newInstance(receiver, arguments);
182197
}
183198

199+
/**
200+
* Executes this value if it {@link #canExecute() can} be executed. If the result value is
201+
* needed use {@link #execute(Object...)} instead.
202+
*
203+
* @throws IllegalStateException if the underlying context was closed
204+
* @throws UnsupportedOperationException if this object cannot be executed.
205+
* @throws PolyglotException if a guest language error occurred during execution.
206+
* @see #execute(Object...)
207+
*
208+
* @since 1.0
209+
*/
210+
public void executeVoid(Object... arguments) {
211+
if (arguments.length == 0) {
212+
// specialized entry point for zero argument execute calls
213+
impl.executeVoid(receiver);
214+
} else {
215+
impl.executeVoid(receiver, arguments);
216+
}
217+
}
218+
184219
/**
185220
*
186221
*

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ protected AbstractContextImpl(AbstractPolyglotImpl impl) {
226226

227227
public abstract void close(boolean interuptExecution);
228228

229+
public abstract void explicitEnter();
230+
231+
public abstract void explicitLeave();
232+
229233
}
230234

231235
public abstract static class AbstractEngineImpl {
@@ -427,6 +431,10 @@ public Value execute(Object receiver, Object[] arguments) {
427431
return executeUnsupported(receiver);
428432
}
429433

434+
public Value execute(Object receiver) {
435+
return executeUnsupported(receiver);
436+
}
437+
430438
public final Value executeUnsupported(Object receiver) {
431439
throw unsupported(receiver, "execute(Object...)", "canExecute()");
432440
}
@@ -443,6 +451,18 @@ public final Value newInstanceUnsupported(Object receiver) {
443451
throw unsupported(receiver, "newInstance(Object...)", "canInstantiate()");
444452
}
445453

454+
public void executeVoid(Object receiver, Object[] arguments) {
455+
executeVoidUnsupported(receiver);
456+
}
457+
458+
public void executeVoid(Object receiver) {
459+
executeVoidUnsupported(receiver);
460+
}
461+
462+
public final void executeVoidUnsupported(Object receiver) {
463+
throw unsupported(receiver, "executeVoid(Object...)", "canExecute()");
464+
}
465+
446466
public boolean isString(Object receiver) {
447467
return false;
448468
}

truffle/src/com.oracle.truffle.api.benchmark/src/com/oracle/truffle/api/benchmark/EngineBenchmark.java

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@
3232
import org.openjdk.jmh.annotations.Benchmark;
3333
import org.openjdk.jmh.annotations.Measurement;
3434
import org.openjdk.jmh.annotations.State;
35+
import org.openjdk.jmh.annotations.TearDown;
3536
import org.openjdk.jmh.annotations.Warmup;
3637

3738
import com.oracle.truffle.api.CallTarget;
3839
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
40+
import com.oracle.truffle.api.Scope;
3941
import com.oracle.truffle.api.Truffle;
4042
import com.oracle.truffle.api.TruffleLanguage;
4143
import com.oracle.truffle.api.TruffleLanguage.Env;
42-
import com.oracle.truffle.api.Scope;
4344
import com.oracle.truffle.api.frame.Frame;
4445
import com.oracle.truffle.api.frame.VirtualFrame;
4546
import com.oracle.truffle.api.interop.ForeignAccess;
@@ -74,7 +75,26 @@ public static class ContextState {
7475
final Context context = Context.create(TEST_LANGUAGE);
7576
final Value value = context.eval(source);
7677
final Integer intValue = 42;
77-
final Value hostValue = context.lookup(TEST_LANGUAGE, "context");
78+
final Value hostValue = context.importSymbol("context");
79+
80+
@TearDown
81+
public void tearDown() {
82+
context.close();
83+
}
84+
}
85+
86+
@State(org.openjdk.jmh.annotations.Scope.Thread)
87+
public static class ContextStateEnterLeave extends ContextState {
88+
89+
public ContextStateEnterLeave() {
90+
context.enter();
91+
}
92+
93+
@Override
94+
public void tearDown() {
95+
context.leave();
96+
super.tearDown();
97+
}
7898
}
7999

80100
@Benchmark
@@ -87,6 +107,21 @@ public void executePolyglot1(ContextState state) {
87107
state.value.execute();
88108
}
89109

110+
@Benchmark
111+
public void executePolyglot1Void(ContextState state) {
112+
state.value.executeVoid();
113+
}
114+
115+
@Benchmark
116+
public void executePolyglot1VoidEntered(ContextStateEnterLeave state) {
117+
state.value.executeVoid();
118+
}
119+
120+
@Benchmark
121+
public Object executePolyglot1CallTarget(CallTargetCallState state) {
122+
return state.callTarget.call(state.internalContext.object);
123+
}
124+
90125
@Benchmark
91126
public int executePolyglot2(ContextState state) {
92127
int result = 0;
@@ -115,11 +150,11 @@ public Object execute(VirtualFrame frame) {
115150
return constant;
116151
}
117152
});
118-
}
119153

120-
@Benchmark
121-
public Object executeCallTarget1(CallTargetCallState state) {
122-
return state.callTarget.call(state.internalContext.object);
154+
@TearDown
155+
public void tearDown() {
156+
context.close();
157+
}
123158
}
124159

125160
@Benchmark
@@ -191,17 +226,23 @@ public Object asHostObject(ContextState state) {
191226
/*
192227
* Test language that ensures that only engine overhead is tested.
193228
*/
194-
@TruffleLanguage.Registration(id = TEST_LANGUAGE, name = "", version = "", mimeType = "")
229+
@TruffleLanguage.Registration(id = TEST_LANGUAGE, name = "", version = "", mimeType = TEST_LANGUAGE)
195230
public static class BenchmarkTestLanguage extends TruffleLanguage<BenchmarkContext> {
196231

197232
@Override
198233
protected BenchmarkContext createContext(Env env) {
199-
return new BenchmarkContext(env, new Function<TruffleObject, Scope>() {
234+
BenchmarkContext context = new BenchmarkContext(env, new Function<TruffleObject, Scope>() {
200235
@Override
201236
public Scope apply(TruffleObject obj) {
202237
return Scope.newBuilder("Benchmark top scope", obj).build();
203238
}
204239
});
240+
return context;
241+
}
242+
243+
@Override
244+
protected void initializeContext(BenchmarkContext context) throws Exception {
245+
context.env.exportSymbol("context", JavaInterop.asTruffleValue(context));
205246
}
206247

207248
@Override
@@ -245,7 +286,6 @@ static class BenchmarkContext {
245286
this.env = env;
246287
topScopes = Collections.singleton(scopeProvider.apply(new TopScopeObject(this)));
247288
}
248-
249289
}
250290

251291
public static class BenchmarkObject implements TruffleObject {

truffle/src/com.oracle.truffle.api.benchmark/src/com/oracle/truffle/api/benchmark/debug/DebugSpeedBench.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
import java.io.IOException;
2626

27+
import org.graalvm.polyglot.Context;
28+
import org.graalvm.polyglot.Source;
2729
import org.openjdk.jmh.annotations.Benchmark;
2830
import org.openjdk.jmh.annotations.Fork;
2931
import org.openjdk.jmh.annotations.Measurement;
@@ -33,9 +35,6 @@
3335
import org.openjdk.jmh.annotations.TearDown;
3436
import org.openjdk.jmh.annotations.Warmup;
3537

36-
import org.graalvm.polyglot.Context;
37-
import org.graalvm.polyglot.Source;
38-
3938
import com.oracle.truffle.api.debug.Debugger;
4039
import com.oracle.truffle.api.debug.DebuggerSession;
4140
import com.oracle.truffle.api.debug.SuspendedCallback;

0 commit comments

Comments
 (0)