Skip to content

Commit ce4d970

Browse files
author
Andrija Kolic
committed
Introduce Summary interface and implementations to Polybench harness.
1 parent 793beba commit ce4d970

File tree

8 files changed

+254
-11
lines changed

8 files changed

+254
-11
lines changed

truffle/mx.truffle/mx_polybench/model.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,8 @@ def rules(self, output, benchmarks, bmSuiteArgs):
520520
if metric_name == "time":
521521
# For metric "time", two metrics are reported:
522522
# - "warmup" (per-iteration data for "warmup" and "run" iterations)
523-
# - "time" (per-iteration data for only the "run" iterations)
523+
# - "time-sample" (per-iteration data for only the "run" iterations)
524+
# - "time" (aggregation of per-iteration data for the "run" iterations after outlier removal)
524525
rules += [
525526
mx_benchmark.StdOutRule(
526527
r"\[.*\] iteration ([0-9]*): (?P<value>.*) (?P<unit>.*)",
@@ -540,7 +541,7 @@ def rules(self, output, benchmarks, bmSuiteArgs):
540541
{
541542
"benchmark": benchmark_name,
542543
"metric.better": "lower",
543-
"metric.name": "time",
544+
"metric.name": "time-sample",
544545
"metric.unit": ("<unit>", str),
545546
"metric.value": ("<value>", float),
546547
"metric.type": "numeric",
@@ -549,6 +550,19 @@ def rules(self, output, benchmarks, bmSuiteArgs):
549550
},
550551
startPattern=r"::: Running :::",
551552
),
553+
ExcludeWarmupRule(
554+
r"\[.*\] run aggregate summary: (?P<value>.*) (?P<unit>.*)",
555+
{
556+
"benchmark": benchmark_name,
557+
"metric.better": "lower",
558+
"metric.name": "time",
559+
"metric.unit": ("<unit>", str),
560+
"metric.value": ("<value>", float),
561+
"metric.type": "numeric",
562+
"metric.score-function": "id",
563+
},
564+
startPattern=r"::: Running :::",
565+
),
552566
]
553567
elif metric_name in ("allocated-memory", "metaspace-memory", "application-memory", "instructions"):
554568
rules += [
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.graalvm.polybench;
26+
27+
import java.util.Optional;
28+
29+
/**
30+
* Summarizes the results of a polybench benchmark with an average value computed from iteration
31+
* datapoints.
32+
*/
33+
class AverageSummary implements Summary {
34+
@Override
35+
public Optional<Double> postprocess(double[] results) {
36+
double sum = 0;
37+
for (Double result : results) {
38+
sum += result;
39+
}
40+
return results.length > 0 ? Optional.of(sum / results.length) : Optional.empty();
41+
}
42+
43+
@Override
44+
public String toString() {
45+
return "AverageSummary{}";
46+
}
47+
}

truffle/src/org.graalvm.polybench/src/org/graalvm/polybench/Config.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.graalvm.polyglot.Value;
2828

29+
import java.io.InvalidObjectException;
2930
import java.util.ArrayList;
3031
import java.util.Collections;
3132
import java.util.HashMap;
@@ -44,6 +45,7 @@ public class Config {
4445
int iterations;
4546
Metric metric;
4647
boolean evalSourceOnlyDefault;
48+
Summary summary;
4749

4850
final List<String> unrecognizedArguments = new ArrayList<>();
4951

@@ -79,7 +81,7 @@ public MultiEngineConfig initMultiEngine() {
7981
return multiEngine;
8082
}
8183

82-
public void parseBenchSpecificDefaults(Value benchmark) {
84+
public void parseBenchSpecificDefaults(Value benchmark) throws InvalidObjectException {
8385
if (warmupIterations == UNINITIALIZED_ITERATIONS) {
8486
if (benchmark.hasMember("warmupIterations")) {
8587
Value warmupIterationsMember = benchmark.getMember("warmupIterations");
@@ -96,6 +98,36 @@ public void parseBenchSpecificDefaults(Value benchmark) {
9698
iterations = DEFAULT_ITERATIONS;
9799
}
98100
}
101+
parseBenchSpecificSummary(benchmark);
102+
}
103+
104+
private void parseBenchSpecificSummary(Value benchmark) throws InvalidObjectException {
105+
if (!benchmark.hasMember("summary")) {
106+
// No 'summary' member provided in the benchmark
107+
return;
108+
}
109+
Value summaryMember = benchmark.getMember("summary");
110+
if (summaryMember.canExecute()) {
111+
summaryMember = summaryMember.execute();
112+
}
113+
if (!summaryMember.hasMember("get")) {
114+
throw new InvalidObjectException("Failed at parsing the 'summary' benchmark member due to it missing a 'get' member!");
115+
}
116+
Value summaryGetMethod = summaryMember.getMember("get");
117+
String summaryClassName = summaryGetMethod.execute("name").asString();
118+
if (OutlierRemovalAverageSummary.class.getSimpleName().equals(summaryClassName)) {
119+
Value lowerThresholdValue = summaryGetMethod.execute("lower-threshold");
120+
Value upperThresholdValue = summaryGetMethod.execute("upper-threshold");
121+
if (!lowerThresholdValue.fitsInDouble() || !upperThresholdValue.fitsInDouble()) {
122+
String msg = "Failed at parsing 'summary' benchmark member with name of '" + summaryClassName + "' due to it missing either a 'lower-threshold' or an 'upper-threshold' member!";
123+
throw new InvalidObjectException(msg);
124+
}
125+
summary = new OutlierRemovalAverageSummary(lowerThresholdValue.asDouble(), upperThresholdValue.asDouble());
126+
} else if (AverageSummary.class.getSimpleName().equals(summaryClassName)) {
127+
summary = new AverageSummary();
128+
} else {
129+
throw new InvalidObjectException("Failed at parsing the 'summary' benchmark member due to unrecognized name of '" + summaryClassName + "'!");
130+
}
99131
}
100132

101133
@Override
@@ -107,7 +139,10 @@ public String toString() {
107139
"iterations: " + (iterations == UNINITIALIZED_ITERATIONS ? "default" : iterations + "\n");
108140
if (multiEngine != null) {
109141
config += "runs: " + multiEngine.numberOfRuns + "\n" +
110-
"shared engine: " + multiEngine.sharedEngine;
142+
"shared engine: " + multiEngine.sharedEngine + "\n";
143+
}
144+
if (summary != null) {
145+
config += "summary: " + summary + "\n";
111146
}
112147
return config;
113148
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.graalvm.polybench;
26+
27+
import java.util.Arrays;
28+
import java.util.Optional;
29+
30+
/**
31+
* Summarizes the results of a polybench benchmark with an average of iteration datapoints that fall
32+
* between `lowerThreshold` and `upperThreshold` percentiles.
33+
*/
34+
class OutlierRemovalAverageSummary extends AverageSummary {
35+
private final double lowerThreshold;
36+
private final double upperThreshold;
37+
38+
OutlierRemovalAverageSummary(double lowerThreshold, double upperThreshold) {
39+
this.lowerThreshold = lowerThreshold;
40+
this.upperThreshold = upperThreshold;
41+
}
42+
43+
@Override
44+
public Optional<Double> postprocess(double[] results) {
45+
if (results.length == 0) {
46+
return Optional.empty();
47+
}
48+
49+
int n = results.length;
50+
Arrays.sort(results);
51+
int fromIndex = (int) Math.ceil(lowerThreshold * n);
52+
int toIndex = (int) Math.floor(upperThreshold * n);
53+
if (fromIndex >= toIndex) {
54+
return Optional.empty();
55+
}
56+
57+
return super.postprocess(Arrays.copyOfRange(results, fromIndex, toIndex));
58+
}
59+
60+
@Override
61+
public String toString() {
62+
return "OutlierRemovalAverageSummary{lowerThreshold=" + lowerThreshold + ", upperThreshold=" + upperThreshold + "}";
63+
}
64+
}

truffle/src/org.graalvm.polybench/src/org/graalvm/polybench/PolyBenchLauncher.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -489,12 +489,12 @@ private void runHarness(Context.Builder contextBuilder, boolean evalSourceOnly,
489489
Workload workload = lookup(context, evalResult.languageId, evalResult.value, "run");
490490

491491
log("::: Running warmup :::");
492-
repeatIterations(context, workload, evalResult.sourceName, true, config.warmupIterations);
492+
repeatIterations(context, workload, evalResult.sourceName, true, config.warmupIterations, config.summary);
493493
log("");
494494

495495
log("::: Running :::");
496496
config.metric.reset();
497-
repeatIterations(context, workload, evalResult.sourceName, false, config.iterations);
497+
repeatIterations(context, workload, evalResult.sourceName, false, config.iterations, config.summary);
498498
log("");
499499
}
500500

@@ -515,9 +515,10 @@ private static String round(double v) {
515515
return String.format("%.2f", v);
516516
}
517517

518-
private void repeatIterations(Context context, Workload workload, String name, boolean warmup, int iterations) {
518+
private void repeatIterations(Context context, Workload workload, String name, boolean warmup, int iterations, Summary summary) {
519519
// Enter explicitly to avoid context switches for each iteration.
520520
context.enter();
521+
double[] iterationResults = new double[iterations];
521522
try {
522523
for (int i = 0; i < iterations; i++) {
523524
config.metric.beforeIteration(warmup, i, config);
@@ -531,14 +532,19 @@ private void repeatIterations(Context context, Workload workload, String name, b
531532
final Optional<Double> value = config.metric.reportAfterIteration(config);
532533
if (value.isPresent()) {
533534
log("[" + name + "] iteration " + i + ": " + round(value.get()) + " " + config.metric.unit());
535+
iterationResults[i] = value.get();
534536
}
535537
}
536538

539+
log("------");
537540
final Optional<Double> value = config.metric.reportAfterAll();
538541
if (value.isPresent()) {
539-
log("------");
540542
log("[" + name + "] " + (warmup ? "after warmup: " : "after run: ") + round(value.get()) + " " + config.metric.unit());
541543
}
544+
Optional<Double> summaryAggregate = summary != null ? summary.postprocess(iterationResults) : Optional.empty();
545+
if (summaryAggregate.isPresent()) {
546+
log("[" + name + "] " + (warmup ? "warmup" : "run") + " aggregate summary: " + round(summaryAggregate.get()) + " " + config.metric.unit());
547+
}
542548
} finally {
543549
context.leave();
544550
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.graalvm.polybench;
26+
27+
import java.util.Optional;
28+
29+
/**
30+
* Summarizes the results of a polybench benchmark with an aggregate metric value computed from
31+
* iteration datapoints.
32+
*/
33+
interface Summary {
34+
Optional<Double> postprocess(double[] results);
35+
}

vm/ci/ci_common/common-bench.jsonnet

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,45 @@ local repo_config = import '../../../ci/repo-configuration.libsonnet';
9999
},
100100

101101
build_polybenchmarks: [
102-
['mx', '--env', '${VM_ENV}', '--dy', 'polybenchmarks', 'sforceimports'],
103-
['mx', '-p', '../../polybenchmarks', 'build_benchmarks'],
104-
['mx', '--dy', 'polybenchmarks', 'build', '--dependencies', 'POLYBENCHMARKS_BENCHMARKS']
102+
['mx', '--env', '${VM_ENV}', '--dy', 'polybenchmarks', 'sforceimports'],
103+
['mx', '-p', '../../polybenchmarks', 'build_benchmarks'],
104+
['mx', '--dy', 'polybenchmarks', 'build', '--dependencies', 'POLYBENCHMARKS_BENCHMARKS']
105105
],
106106

107+
polybench_vm_post_merge(os, arch, language, vm_config, name = null): vm_common.vm_base(os, arch, 'post-merge', bench=true) + self.polybench_vm_common(os, arch) + vm.vm_java_Latest + {
108+
name: utils.hyphenize(['post-merge-bench-vm', vm.vm_setup.short_name, 'polybench', language, name, vm_config, utils.jdk_and_hardware(self)]),
109+
setup+: [
110+
['mx', '--dy', 'graalpython', 'build']
111+
],
112+
local benchmarks = ['interpreter/sieve.py', 'interpreter/fibonacci.py', 'interpreter/deltablue.py', 'interpreter/richards.py'],
113+
run+: [
114+
['grep', '-q', '-E', 'polybench:' + benchmark + ' ([a-z0-9]|-)+: FAILURE', 'split-run.txt'] +
115+
['||'] +
116+
['hwloc-bind', '--cpubind', 'node:0.core:0-3.pu:0', '--membind', 'node:0', '--'] +
117+
['mx', '--env', '${VM_ENV}', '--dy', '/truffle-enterprise', '--dy', 'graalpython', '--kill-with-sigquit'] +
118+
['--java-home', '${POLYBENCH_JVM}'] +
119+
['benchmark', 'polybench:' + benchmark, '--results-file=' + self.result_file, '--append-results'] +
120+
['--', '--jvm=native-image-java-home', '--jvm-config=' + vm_config] +
121+
['-Dnative-image.benchmark.extra-image-build-argument=-R:MaxHeapSize=500m'] +
122+
['-Dnative-image.benchmark.extra-image-build-argument=-J-Xmx24g'] +
123+
['-Dnative-image.benchmark.benchmark-output-dir=mxbuild'] +
124+
['--split-run', 'split-run.txt'] +
125+
['--', '--experimental-options', '--engine.Compilation=false'] +
126+
['||'] +
127+
['echo', 'polybench:' + benchmark + ' general: FAILURE', '>>', 'split-run.txt']
128+
for benchmark in benchmarks
129+
] + [
130+
['set-export', 'BENCH_EXIT_STATUS', '1'],
131+
['cat', 'split-run.txt'],
132+
['grep', '-q', 'FAILURE', 'split-run.txt', '||', 'set-export', 'BENCH_EXIT_STATUS', '0'],
133+
['exit', '$BENCH_EXIT_STATUS']
134+
],
135+
teardown: [self.upload],
136+
notify_groups +: ['python'],
137+
notify_emails+: ['[email protected]'],
138+
timelimit: '4:00:00',
139+
},
140+
107141
js_bench_compilation_throughput(pgo): self.vm_bench_common + common.heap.default + {
108142
local mx_libgraal = ["mx", "--env", repo_config.vm.mx_env.libgraal],
109143

@@ -275,6 +309,10 @@ local repo_config = import '../../../ci/repo-configuration.libsonnet';
275309
],
276310
notify_groups +: ['python'],
277311
}
312+
] + [
313+
# PR-bench Python Interpreter mx benchmark polybench jobs
314+
self.polybench_vm_post_merge('linux', 'amd64', 'python', vm_config, 'interpreter')
315+
for vm_config in ['default', 'g1gc']
278316
] + [
279317
# NFI polybench jobs
280318
self.polybench_vm_gate('linux', 'amd64', 'nfi') + {

vm/mx.vm/suite.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@
6565
},
6666
{
6767
"name": "graalpython",
68+
<<<<<<< HEAD
6869
"version": "b283db0979c0d24da6f2ddd55879ab6f93c0fcbd",
70+
=======
71+
"version": "2315bc2d2e070546bf3a356c2ca6e4ba2455c7c8",
72+
>>>>>>> 3aa5450d718 (Introduce Summary interface and implementations to Polybench harness.)
6973
"dynamic": True,
7074
"urls": [
7175
{"url": "https://github.com/graalvm/graalpython.git", "kind": "git"},

0 commit comments

Comments
 (0)