Created
June 25, 2025 00:26
-
-
Save JohnTortugo/4a78430701869059957c89b21367e235 to your computer and use it in GitHub Desktop.
GraalVM Truffle Proxies
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.graalvm.polyglot.Context; | |
import org.graalvm.polyglot.Engine; | |
import org.graalvm.polyglot.Source; | |
import org.graalvm.polyglot.Value; | |
import org.graalvm.polyglot.proxy.ProxyObject; | |
public class ProxyFieldAccess { | |
private static final int BENCH_ITERATIONS = 11000; | |
private static long[] times = new long[BENCH_ITERATIONS]; | |
private static Double[] results = new Double[BENCH_ITERATIONS]; | |
private static final String jsSource = """ | |
( | |
function simple_sum(order, length) { | |
let result = 0; | |
for (var i = 0; i < length; i++) { | |
result += 3.1415 * order.quantity; | |
} | |
return result; | |
} | |
) | |
"""; | |
public static void computeStats(String label, long[] data, int N) { | |
if (data == null || data.length == 0 || N <= 1 || N > data.length) { | |
throw new IllegalArgumentException("Invalid input or N too large/small"); | |
} | |
int startIndex = data.length - N; | |
double sum = 0.0; | |
for (int i = startIndex; i < data.length; i++) { | |
sum += data[i]; | |
} | |
double average = sum / N; | |
double varianceSum = 0.0; | |
for (int i = startIndex; i < data.length; i++) { | |
double diff = data[i] - average; | |
varianceSum += diff * diff; | |
} | |
double standardDeviation = Math.sqrt(varianceSum / (N - 1)); // sample stdev | |
double standardError = standardDeviation / Math.sqrt(N); | |
double zScore = 1.96; // for 95% confidence | |
double confidenceLow = average - zScore * standardError; | |
double confidenceHigh = average + zScore * standardError; | |
System.out.println(label); | |
System.out.printf("Average: %.2f us%n", average); | |
System.out.printf("Standard Deviation: %.2f us%n", standardDeviation); | |
System.out.printf("95%% Confidence Interval: [%.2f, %.2f] us%n", confidenceLow, confidenceHigh); | |
} | |
public static class DirectField { | |
public Integer quantity = 0; | |
public DirectField(int quantity) { | |
this.quantity = quantity; | |
} | |
} | |
public static void directFieldAccess() { | |
final Engine engine = Engine.newBuilder("js") | |
.allowExperimentalOptions(true) | |
.option("engine.DynamicCompilationThresholds", "false") | |
.option("engine.BackgroundCompilation", "false") | |
.option("engine.OSR", "false") | |
.build(); | |
var source = Source.newBuilder("js", jsSource, "direct_field_access.js").buildLiteral(); | |
try (var context = Context.newBuilder("js").engine(engine).allowAllAccess(true).build()) { | |
var function = context.eval(source); | |
for (int i = 0; i < BENCH_ITERATIONS; i++) { | |
var order = new DirectField(i); | |
var start = System.nanoTime(); | |
results[i] = function.execute((Object) order, 100_000).asDouble(); | |
times[i] = (System.nanoTime() - start) / 1_000; | |
} | |
} | |
computeStats("DirectFieldAccess", times, 1000); | |
} | |
public static class SingleFieldProxy implements ProxyObject { | |
Integer quantity = 0; | |
public SingleFieldProxy(int quantity) { | |
this.quantity = quantity; | |
} | |
@Override | |
public Object getMember(final String key) { | |
return quantity; | |
} | |
@Override | |
public Object getMemberKeys() { | |
return new String[] {"quantity"}; | |
} | |
@Override | |
public boolean hasMember(final String key) { | |
return key.equals("quantity"); | |
} | |
@Override | |
public void putMember(final String key, final Value value) { | |
throw new UnsupportedOperationException("SingleFieldProxy is immutable"); | |
} | |
} | |
public static void singleFieldProxyAccess() throws Exception { | |
final Engine engine = Engine.newBuilder("js") | |
.allowExperimentalOptions(true) | |
.option("engine.DynamicCompilationThresholds", "false") | |
.option("engine.BackgroundCompilation", "false") | |
.option("engine.OSR", "false") | |
.build(); | |
var source = Source.newBuilder("js", jsSource, "single_field_proxy_access.js").buildLiteral(); | |
try (var context = Context.newBuilder("js").engine(engine).allowAllAccess(true).build()) { | |
var function = context.eval(source); | |
for (int i = 0; i < BENCH_ITERATIONS; i++) { | |
var order = new SingleFieldProxy(i); | |
var start = System.nanoTime(); | |
results[i] = function.execute((Object) order, 100_000).asDouble(); | |
times[i] = (System.nanoTime() - start) / 1_000; | |
} | |
} | |
computeStats("SingleFieldProxyAccess", times, 1000); | |
} | |
public static void main(String[] args) throws Exception { | |
System.out.println("-----------------------------------------"); | |
directFieldAccess(); | |
System.out.println("-----------------------------------------"); | |
singleFieldProxyAccess(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment