Skip to content

Commit cbe5cbd

Browse files
author
Andrija Kolic
committed
[GR-69564] [GR-69910] [GR-69767] Implement iteration summaries in the Polybench harness
PullRequest: graal/22204
2 parents b862bbf + d4944b5 commit cbe5cbd

File tree

8 files changed

+217
-14
lines changed

8 files changed

+217
-14
lines changed

graal-common.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"README": "This file contains definitions that are useful for the jsonnet CI files of the graal and graal-enterprise repositories.",
33
"ci": {
4-
"overlay": "49f8376ffc0773e2069aea7be211e06eafc1088b"
4+
"overlay": "aecf4e4409bde4bfcc1009e5b8c895e150500dfa"
55
}
66
}

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: 38 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,37 @@ 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 + "'";
123+
msg += " because 'lower-threshold' or 'upper-threshold' is not present, or does not fit into a double!";
124+
throw new InvalidObjectException(msg);
125+
}
126+
summary = new OutlierRemovalAverageSummary(lowerThresholdValue.asDouble(), upperThresholdValue.asDouble());
127+
} else if (AverageSummary.class.getSimpleName().equals(summaryClassName)) {
128+
summary = new AverageSummary();
129+
} else {
130+
throw new InvalidObjectException("Failed at parsing the 'summary' benchmark member due to unrecognized name of '" + summaryClassName + "'!");
131+
}
99132
}
100133

101134
@Override
@@ -107,7 +140,10 @@ public String toString() {
107140
"iterations: " + (iterations == UNINITIALIZED_ITERATIONS ? "default" : iterations + "\n");
108141
if (multiEngine != null) {
109142
config += "runs: " + multiEngine.numberOfRuns + "\n" +
110-
"shared engine: " + multiEngine.sharedEngine;
143+
"shared engine: " + multiEngine.sharedEngine + "\n";
144+
}
145+
if (summary != null) {
146+
config += "summary: " + summary + "\n";
111147
}
112148
return config;
113149
}
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: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,14 @@ local repo_config = import '../../../ci/repo-configuration.libsonnet';
6969
# Extends the provided polybench command with common arguments used in CI. We want the command at the call site
7070
# to be simple (i.e., a flat array of string literals) so it can be easily copied and run locally; using this
7171
# wrapper allows us to inject CI-specific fields without specifying them in the command.
72+
hwloc_command_prefix:: if std.length(std.find('bench', self.targets)) > 0 then ["hwloc-bind", "--cpubind", "node:0", "--membind", "node:0", "--"] else [],
7273
polybench_wrap(command)::
7374
assert command[0] == 'mx' : "polybench command should start with 'mx'";
7475
// Dynamically import /truffle-enterprise when running on enterprise.
7576
local extra_imports = if is_enterprise then ['--dy', '/truffle-enterprise'] else [];
76-
['mx'] + extra_imports + command[1:] + ['--mx-benchmark-args', '--results-file', self.result_file] +
77+
self.hwloc_command_prefix + ['mx'] + extra_imports + command[1:] + ['--mx-benchmark-args', '--results-file', self.result_file] +
7778
(if (fail_fast) then ['--fail-fast'] else []),
78-
notify_groups:: ['polybench'],
79+
notify_groups:: ['polybench']
7980
},
8081

8182
polybench_vm_hpc_common: self.polybench_vm_common('linux', 'amd64', skip_machine=true) + self.polybench_hpc_linux_common(shape='e4_8_64') + {
@@ -99,9 +100,9 @@ local repo_config = import '../../../ci/repo-configuration.libsonnet';
99100
},
100101

101102
build_polybenchmarks: [
102-
['mx', '--env', '${VM_ENV}', '--dy', 'polybenchmarks', 'sforceimports'],
103-
['mx', '-p', '../../polybenchmarks', 'build_benchmarks'],
104-
['mx', '--dy', 'polybenchmarks', 'build', '--dependencies', 'POLYBENCHMARKS_BENCHMARKS']
103+
['mx', '--env', '${VM_ENV}', '--dy', 'polybenchmarks', 'sforceimports'],
104+
['mx', '-p', '../../polybenchmarks', 'build_benchmarks'],
105+
['mx', '--dy', 'polybenchmarks', 'build', '--dependencies', 'POLYBENCHMARKS_BENCHMARKS']
105106
],
106107

107108
js_bench_compilation_throughput(pgo): self.vm_bench_common + common.heap.default + {

0 commit comments

Comments
 (0)