Skip to content

Commit 8b637aa

Browse files
committed
Add coremark-wasm as a benchmark
This is a non-timing benchmark, and the output value is shown as a measure of performance (higher = better)
1 parent d151be8 commit 8b637aa

File tree

2 files changed

+52
-23
lines changed

2 files changed

+52
-23
lines changed

WasmCoremark/coremark-minimal.wasm

7.59 KB
Binary file not shown.

run.py

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,57 @@
11
#!/usr/bin/env python3
22

33
import argparse
4+
import enum
45
import json
56
import os
6-
import subprocess
7+
import re
8+
import shlex
79
import statistics
10+
import subprocess
811
import sys
912
from tabulate import tabulate
1013

11-
def run_benchmark(executable, executable_arguments, suite, test_file, iterations, index, total, suppress_output=False):
12-
times = []
14+
FLOAT_RE = re.compile(r"([0-9]*\.[0-9]+|[0-9]+)")
15+
16+
class ScoreMetric(enum.Enum):
17+
time = "time"
18+
output = "reported_score"
19+
20+
def run_benchmark(executable, executable_arguments, suite, test_file, score_metric, iterations, index, total, suppress_output=False):
21+
unit = "s" if score_metric == ScoreMetric.time else ""
22+
measures = { k:[] for k in ScoreMetric }
23+
1324
for i in range(iterations):
1425
if not suppress_output:
15-
print(f"[{index}/{total}] {suite}/{test_file} (Iteration {i+1}/{iterations}, Avg: {statistics.mean(times):.3f}s)" if times else f"[{index}/{total}] {suite}/{test_file} (Iteration {i+1}/{iterations})", end="\r")
26+
print(f"[{index}/{total}] {suite}/{test_file} (Iteration {i+1}/{iterations}, Avg: {statistics.mean(measures[score_metric]):.3f}{unit})" if measures[score_metric] else f"[{index}/{total}] {suite}/{test_file} (Iteration {i+1}/{iterations})", end="\r")
1627
sys.stdout.flush()
1728

18-
result = subprocess.run([f"time -p {executable} {' '.join(executable_arguments)} {suite}/{test_file}"], shell=True, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, text=True, executable="/bin/bash")
29+
result = subprocess.run([f"time -p {shlex.quote(executable)} {' '.join(shlex.quote(arg) for arg in executable_arguments)} {suite}/{test_file}"], shell=True, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL if score_metric == ScoreMetric.time else subprocess.PIPE, text=True, executable="/bin/bash")
1930
result.check_returncode()
2031

2132
time_output = result.stderr.split("\n")
2233
real_time_line = [line for line in time_output if "real" in line][0]
2334
time_taken = float(real_time_line.split(" ")[-1])
24-
times.append(time_taken)
25-
26-
mean = statistics.mean(times)
27-
stdev = statistics.stdev(times) if len(times) > 1 else 0
28-
min_time = min(times)
29-
max_time = max(times)
35+
measures[ScoreMetric.time].append(time_taken)
36+
37+
if score_metric == ScoreMetric.output:
38+
output = result.stdout.split("\n")
39+
value = None
40+
for line in output:
41+
if match := FLOAT_RE.search(line):
42+
value = float(match[1])
43+
assert value is not None, "Expected a float in the benchmark output"
44+
measures[ScoreMetric.output].append(value)
45+
46+
means = { key:statistics.mean(values) if len(values) > 0 else None for key, values in measures.items() }
47+
stdevs = { key:statistics.stdev(values) if len(values) > 1 else 0 for key, values in measures.items() }
48+
min_values = { key:min(values) if len(values) > 0 else None for key, values in measures.items() }
49+
max_values = { key:max(values) if len(values) > 0 else None for key, values in measures.items() }
3050
if not suppress_output:
31-
print(f"[{index}/{total}] {suite}/{test_file} completed. Mean: {mean:.3f}s ± {stdev:.3f}s, Range: {min_time:.3f}s{max_time:.3f}s\033[K")
51+
print(f"[{index}/{total}] {suite}/{test_file} completed. Mean: {means[score_metric]:.3f}{unit} ± {stdevs[score_metric]:.3f}{unit}, Range: {min_values[score_metric]:.3f}{unit}{max_values[score_metric]:.3f}{unit}\033[K")
3252
sys.stdout.flush()
3353

34-
return mean, stdev, min_time, max_time, times
54+
return means, stdevs, min_values, max_values, measures
3555

3656
def main():
3757
parser = argparse.ArgumentParser(description="Run JavaScript benchmarks.")
@@ -44,7 +64,7 @@ def main():
4464
args = parser.parse_args()
4565

4666
if args.suites == "all":
47-
suites = ["SunSpider", "Kraken", "Octane", "JetStream", "JetStream3", "RegExp", "MicroBench", "WasmMicroBench"]
67+
suites = ["SunSpider", "Kraken", "Octane", "JetStream", "JetStream3", "RegExp", "MicroBench", "WasmMicroBench", "WasmCoremark"]
4868
else:
4969
suites = args.suites.split(",")
5070

@@ -54,7 +74,7 @@ def main():
5474
for test_file in sorted(os.listdir("SunSpider")):
5575
if not test_file.endswith(".js"):
5676
continue
57-
run_benchmark(args.executable, [], "SunSpider", test_file, 1, 0, 0, suppress_output=True)
77+
run_benchmark(args.executable, [], "SunSpider", ScoreMetric.time, test_file, 1, 0, 0, suppress_output=True)
5878

5979
results = {}
6080
table_data = []
@@ -64,32 +84,41 @@ def main():
6484
for suite in suites:
6585
results[suite] = {}
6686
is_wasm_bench = suite == "WasmMicroBench"
87+
is_wasm_coremark = suite == "WasmCoremark"
6788

6889
executable = ""
6990
executable_arguments = []
91+
score_metric = ScoreMetric.time
7092
if (is_wasm_bench):
7193
executable = args.wasm_executable
7294
executable_arguments = ["-e", "run_microbench"]
95+
elif is_wasm_coremark:
96+
executable = args.wasm_executable
97+
executable_arguments = ["-e", "run", "--export-js", "env.clock_ms:i64=BigInt(+new Date)"]
98+
score_metric = ScoreMetric.output
7399
else:
74100
executable = args.executable
75101

76102
for test_file in sorted(os.listdir(suite)):
77-
if (is_wasm_bench):
103+
if (is_wasm_bench or is_wasm_coremark):
78104
if not test_file.endswith(".wasm"):
79105
continue
80106
else:
81107
if not test_file.endswith(".js"):
82108
continue
83109

84-
mean, stdev, min_time, max_time, runs = run_benchmark(executable, executable_arguments, suite, test_file, args.iterations, current_test, total_tests)
110+
stats = run_benchmark(executable, executable_arguments, suite, test_file, score_metric, args.iterations, current_test, total_tests)
85111
results[suite][test_file] = {
86-
"mean": mean,
87-
"stdev": stdev,
88-
"min": min_time,
89-
"max": max_time,
90-
"runs": runs
112+
key.value: {
113+
"mean": mean,
114+
"stdev": stdev,
115+
"min": min_val,
116+
"max": max_val,
117+
"runs": runs,
118+
} for key, (mean, stdev, min_val, max_val, runs) in zip(stats[0].keys(), zip(*(x.values() for x in stats))) if runs
91119
}
92-
table_data.append([suite, test_file, f"{mean:.3f} ± {stdev:.3f}", f"{min_time:.3f}{max_time:.3f}"])
120+
mean, stdev, min_val, max_val, _ = (stat[score_metric] for stat in stats)
121+
table_data.append([suite, test_file, f"{mean:.3f} ± {stdev:.3f}", f"{min_val:.3f}{max_val:.3f}"])
93122
current_test += 1
94123

95124
print(tabulate(table_data, headers=["Suite", "Test", "Mean ± σ", "Range (min … max)"]))

0 commit comments

Comments
 (0)