Skip to content

Commit 5b25f63

Browse files
committed
[Benchmarks] add RR benchmarks with test
1 parent 8b4629c commit 5b25f63

File tree

5 files changed

+276
-5
lines changed

5 files changed

+276
-5
lines changed

.github/workflows/sycl-ur-perf-benchmarking.yml

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ jobs:
135135
build_sycl_dispatch:
136136
name: '[Dispatch] Build SYCL'
137137
needs: [ sanitize_inputs_dispatch ]
138+
if: false
138139
uses: ./.github/workflows/sycl-linux-build.yml
139140
with:
140141
build_ref: ${{ needs.sanitize_inputs_dispatch.outputs.build_ref }}
@@ -224,7 +225,7 @@ jobs:
224225
# Benchmark framework builds and runs on PRs path:
225226
build_pr:
226227
name: '[PR] Build SYCL'
227-
if: github.event_name == 'pull_request'
228+
if: false && github.event_name == 'pull_request'
228229
uses: ./.github/workflows/sycl-linux-build.yml
229230
with:
230231
build_ref: ${{ github.sha }}
@@ -237,6 +238,38 @@ jobs:
237238
changes: '[]'
238239
toolchain_artifact: sycl_linux_default
239240

241+
unittest_pr:
242+
name: '[PR] SYCL Unit Tests'
243+
if: github.event_name == 'pull_request'
244+
# needs: [build_pr]
245+
runs-on: PVC_PERF
246+
container:
247+
image: 'ghcr.io/intel/llvm/sycl_ubuntu2404_nightly:latest'
248+
env:
249+
LLVM_BENCHMARKS_UNIT_TESTING: 1
250+
CMPLR_ROOT: ./toolchain
251+
steps:
252+
- name: Checkout code
253+
uses: actions/checkout@v3
254+
- name: Install python libraries and dependencies
255+
run: |
256+
pip install --user --break-system-packages -r ./devops/scripts/benchmarks/requirements.txt
257+
- name: Checkout compute benchmarks code
258+
uses: actions/checkout@v3
259+
with:
260+
repository: intel/compute-benchmarks
261+
ref: master
262+
path: compute-benchmarks
263+
- name: Build Compute Benchmarks
264+
run: |
265+
mkdir build && cd build
266+
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DNULL_L0=ON ../compute-benchmarks
267+
cmake --build . -- -j$(nproc)
268+
- name: Run tests
269+
run: |
270+
COMPUTE_BENCHMARK_BUILD_PATH=compute-benchmarks/build python3 ./devops/scripts/benchmarks/tests/test_integration.py
271+
272+
240273
# TODO: When we have stable BMG runner(s), consider moving this job to that runner.
241274
test_benchmark_framework:
242275
name: '[PR] Benchmark suite testing'

devops/scripts/benchmarks/benches/compute.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,48 @@ def benchmarks(self) -> list[Benchmark]:
269269
)
270270
)
271271

272+
record_and_replay_params = product([0, 1], [0, 1])
273+
for emulate, instantiate in record_and_replay_params:
274+
275+
def createRrBench(**kwargs):
276+
return RecordAndReplay(
277+
self,
278+
RUNTIMES.LEVEL_ZERO,
279+
PROFILERS.TIMER,
280+
mRec=1,
281+
mInst=instantiate,
282+
mDest=0,
283+
emulate=emulate,
284+
**kwargs,
285+
)
286+
287+
benches += [
288+
createRrBench(
289+
nForksInLvl=2,
290+
nLvls=4,
291+
nCmdSetsInLvl=10,
292+
nInstantiations=10,
293+
nAppendKern=10,
294+
nAppendCopy=1,
295+
),
296+
createRrBench(
297+
nForksInLvl=1,
298+
nLvls=1,
299+
nCmdSetsInLvl=10,
300+
nInstantiations=10,
301+
nAppendKern=10,
302+
nAppendCopy=10,
303+
),
304+
createRrBench(
305+
nForksInLvl=1,
306+
nLvls=4,
307+
nCmdSetsInLvl=1,
308+
nInstantiations=0,
309+
nAppendKern=1,
310+
nAppendCopy=0,
311+
),
312+
]
313+
272314
# Add UR-specific benchmarks
273315
benches += [
274316
# TODO: multithread_benchmark_ur fails with segfault
@@ -647,6 +689,43 @@ def bin_args(self, run_trace: TracingType = TracingType.NONE) -> list[str]:
647689
]
648690

649691

692+
class RecordAndReplay(ComputeBenchmark):
693+
def __init__(self, bench, runtime: RUNTIMES, profiler_type, **kwargs):
694+
self.rr_params = kwargs
695+
self.iterations_regular = 1000
696+
self.iterations_trace = 10
697+
super().__init__(
698+
bench,
699+
f"record_and_replay_benchmark_{runtime.value}",
700+
"RecordGraph",
701+
runtime,
702+
profiler_type,
703+
)
704+
705+
def name(self):
706+
ret = []
707+
for k, v in self.rr_params.items():
708+
if k[0] == "n": # numeric parameter
709+
ret.append(f"{k[1:]} {v}")
710+
elif k[0] == "m":
711+
if v != 0: # measure parameter
712+
ret.append(f"{k[1:]}")
713+
else: # boolean parameter
714+
if v != 0:
715+
ret.append(k)
716+
ret.sort()
717+
return f"L0 {self.test} " + ", ".join(ret)
718+
719+
def display_name(self) -> str:
720+
return self.name()
721+
722+
def get_tags(self):
723+
return ["L0"]
724+
725+
def bin_args(self, run_trace: TracingType = TracingType.NONE) -> list[str]:
726+
return [f"--{k}={v}" for k, v in self.rr_params.items()]
727+
728+
650729
class QueueInOrderMemcpy(ComputeBenchmark):
651730
def __init__(self, bench, isCopyOnly, source, destination, size, profiler_type):
652731
self.isCopyOnly = isCopyOnly

devops/scripts/benchmarks/git_project.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
# See LICENSE.TXT
44
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
55

6+
import os
67
from pathlib import Path
78
import shutil
89

910
from utils.logger import log
1011
from utils.utils import run
1112
from options import options
1213

13-
1414
class GitProject:
1515
def __init__(
1616
self,
@@ -167,6 +167,11 @@ def _setup_repo(self) -> bool:
167167
Returns:
168168
bool: True if the repository was cloned or updated, False if it was already up-to-date.
169169
"""
170+
if os.environ.get("LLVM_BENCHMARKS_UNIT_TESTING") == "1":
171+
log.debug(
172+
f"Skipping git operations during unit testing of {self._name} (LLVM_BENCHMARKS_UNIT_TESTING=1)."
173+
)
174+
return False
170175
if not self.src_dir.exists():
171176
self._git_clone()
172177
return True

devops/scripts/benchmarks/main.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,13 @@ def process_results(
137137
stddev_threshold_override
138138
if stddev_threshold_override is not None
139139
else options.stddev_threshold
140-
) * mean_value
140+
)
141+
threshold_scaled = threshold * mean_value
141142

142-
if stddev > threshold:
143-
log.warning(f"stddev {stddev} above the threshold {threshold} for {label}")
143+
if stddev > threshold_scaled:
144+
log.warning(
145+
f"stddev {stddev} above the threshold {threshold_scaled} ({threshold} times {mean_value}) for {label}"
146+
)
144147
valid_results = False
145148

146149
rlist.sort(key=lambda res: res.value)
@@ -228,6 +231,9 @@ def main(directory, additional_env_vars, compare_names, filter):
228231
benchmark for benchmark in s.benchmarks() if benchmark.enabled()
229232
]
230233
if filter:
234+
log.debug(
235+
f"Filtering {len(suite_benchmarks)} benchmarks in {s.name()} suite for {filter.pattern}"
236+
)
231237
suite_benchmarks = [
232238
benchmark
233239
for benchmark in suite_benchmarks
@@ -713,6 +719,7 @@ def validate_and_parse_env_args(env_args):
713719
options.dry_run = args.dry_run
714720
options.umf = args.umf
715721
options.iterations_stddev = args.iterations_stddev
722+
options.stddev_threshold = args.stddev_threshold
716723
options.build_igc = args.build_igc
717724
options.current_run_name = args.relative_perf
718725
options.cudnn_directory = args.cudnn_directory
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Copyright (C) 2025 Intel Corporation
2+
# Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
3+
# See LICENSE.TXT
4+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
6+
import os
7+
import shutil
8+
import unittest
9+
import tempfile
10+
import subprocess
11+
import json
12+
from collections import namedtuple
13+
14+
# oneapi has to be installed and sourced for sycl benchmarks tests
15+
16+
DataJson = namedtuple("DataJson", ["runs", "metadata", "tags", "names"])
17+
DataJsonRun = namedtuple("DataJsonRun", ["name", "results"])
18+
DataJsonResult = namedtuple(
19+
"DataJsonResult", ["name", "label", "suite", "value", "unit"]
20+
)
21+
22+
class App:
23+
def __init__(self):
24+
self.OUTPUT_DIR = None
25+
self.RESULTS_DIR = None
26+
self.WORKDIR_DIR = None
27+
28+
def prepare_dirs(self):
29+
self.OUTPUT_DIR = tempfile.mkdtemp()
30+
self.RESULTS_DIR = tempfile.mkdtemp()
31+
self.WORKDIR_DIR = tempfile.mkdtemp()
32+
33+
# when UT does not want to build compute-benchmarks from scratch, it can provide prebuilt path
34+
cb_targetpath = os.environ.get("COMPUTE_BENCHMARKS_BUILD_PATH")
35+
if cb_targetpath and os.path.isdir(cb_targetpath):
36+
cb_build_dir = os.path.join(self.WORKDIR_DIR, "compute-benchmarks-build")
37+
os.symlink(cb_targetpath, cb_build_dir)
38+
with open(
39+
os.path.join(self.WORKDIR_DIR, "BENCH_WORKDIR_VERSION"), "w"
40+
) as f:
41+
f.write("2.0") # TODO: take from main.INTERNAL_WORKDIR_VERSION
42+
43+
def remove_dirs(self):
44+
for d in [self.RESULTS_DIR, self.OUTPUT_DIR, self.WORKDIR_DIR]:
45+
if d is not None:
46+
shutil.rmtree(d, ignore_errors=True)
47+
48+
def run_main(self, *args):
49+
50+
# TODO: not yet tested: "--detect-version", "sycl,compute_runtime"
51+
52+
return subprocess.run(
53+
[
54+
"./devops/scripts/benchmarks/main.py",
55+
self.WORKDIR_DIR,
56+
"--sycl",
57+
os.environ.get("CMPLR_ROOT"),
58+
"--save",
59+
"testfile",
60+
"--output-html",
61+
"remote",
62+
"--results-dir",
63+
self.RESULTS_DIR,
64+
"--output-dir",
65+
self.OUTPUT_DIR,
66+
"--preset",
67+
"Minimal",
68+
"--timestamp-override",
69+
"20240102_030405",
70+
"--stddev-threshold",
71+
"999999999.9",
72+
"--exit-on-failure",
73+
*args,
74+
]
75+
)
76+
77+
def get_output(self):
78+
with open(output_file) as f:
79+
out = json.load(f)
80+
return DataJson(
81+
runs=[
82+
DataJsonRun(
83+
name=run["name"],
84+
results=[
85+
DataJsonResult(
86+
name=r["name"],
87+
label=r["label"],
88+
suite=r["suite"],
89+
value=r["value"],
90+
unit=r["unit"],
91+
)
92+
for r in run["results"]
93+
],
94+
)
95+
for run in out["benchmarkRuns"]
96+
],
97+
metadata=out["benchmarkMetadata"],
98+
tags=out["benchmarkTags"],
99+
names=out["defaultCompareNames"],
100+
)
101+
102+
103+
# add "--verbose" for debug logs
104+
105+
106+
class TestE2E(unittest.TestCase):
107+
def setUp(self):
108+
# Load test data
109+
self.app = App()
110+
self.app.remove_dirs()
111+
self.app.prepare_dirs()
112+
113+
# clean directory with input, output
114+
115+
def tearDown(self):
116+
self.app.remove_dirs()
117+
118+
def test_record_and_replay(self):
119+
caseName = "L0 RecordGraph AppendCopy 1, AppendKern 10, CmdSetsInLvl 10, ForksInLvl 2, Instantiations 10, Lvls 4, Rec"
120+
run_result = self.app.run_main("--filter", caseName + "$")
121+
self.assertEqual(run_result.returncode, 0, "Subprocess did not exit cleanly")
122+
123+
out = self.app.get_output()
124+
125+
self.assertIn(caseName, [r.name for r in out.runs[0].results])
126+
127+
metadata = out.metadata[caseName]
128+
self.assertEqual(metadata["type"], "benchmark")
129+
self.assertEqual(set(metadata["tags"]), {"L0"})
130+
131+
def test_submit_kernel(self):
132+
caseName = "SubmitKernel out of order with measure completion KernelExecTime=20"
133+
run_result = self.app.run_main("--filter", caseName + "$")
134+
self.assertEqual(run_result.returncode, 0, "Subprocess did not exit cleanly")
135+
136+
out = self.app.get_output()
137+
138+
testName = "api_overhead_benchmark_l0 " + caseName
139+
self.assertIn(testName, [r.name for r in out.runs[0].results])
140+
141+
metadata = out.metadata[testName]
142+
self.assertEqual(metadata["type"], "benchmark")
143+
self.assertEqual(set(metadata["tags"]), {"L0", "latency", "micro", "submit"})
144+
145+
146+
if __name__ == "__main__":
147+
unittest.main()

0 commit comments

Comments
 (0)