Skip to content

[UR][Benchmarks] Support for Unitrace for all benchmarks #19320

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jul 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 42 additions & 10 deletions devops/scripts/benchmarks/benches/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# See LICENSE.TXT
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

from dataclasses import dataclass
import os
import shutil
import subprocess
Expand All @@ -12,6 +11,8 @@
from options import options
from utils.utils import download, run
from abc import ABC, abstractmethod
from utils.unitrace import get_unitrace
from utils.logger import log

benchmark_tags = [
BenchmarkTag("SYCL", "Benchmark uses SYCL runtime"),
Expand Down Expand Up @@ -61,6 +62,12 @@ def enabled(self) -> bool:
By default, it returns True, but can be overridden to disable a benchmark."""
return True

def traceable(self) -> bool:
"""Returns whether this benchmark should be traced by Unitrace.
By default, it returns True, but can be overridden to disable tracing for a benchmark.
"""
return True

@abstractmethod
def setup(self):
pass
Expand All @@ -70,11 +77,12 @@ def teardown(self):
pass

@abstractmethod
def run(self, env_vars) -> list[Result]:
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
"""Execute the benchmark with the given environment variables.

Args:
env_vars: Environment variables to use when running the benchmark.
run_unitrace: Whether to run benchmark under Unitrace.

Returns:
A list of Result objects with the benchmark results.
Expand All @@ -97,7 +105,14 @@ def get_adapter_full_path():
), f"could not find adapter file {adapter_path} (and in similar lib paths)"

def run_bench(
Copy link
Preview

Copilot AI Jul 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Avoid mutable default arguments like extra_unitrace_opt=[]; use None and set to an empty list inside the function to prevent unintended sharing.

Copilot uses AI. Check for mistakes.

self, command, env_vars, ld_library=[], add_sycl=True, use_stdout=True
self,
command,
env_vars,
ld_library=[],
add_sycl=True,
use_stdout=True,
run_unitrace=False,
extra_unitrace_opt=None,
):
env_vars = env_vars.copy()
if options.ur is not None:
Expand All @@ -110,13 +125,30 @@ def run_bench(
ld_libraries = options.extra_ld_libraries.copy()
ld_libraries.extend(ld_library)

result = run(
command=command,
env_vars=env_vars,
add_sycl=add_sycl,
cwd=options.benchmark_cwd,
ld_library=ld_libraries,
)
if self.traceable() and run_unitrace:
if extra_unitrace_opt is None:
extra_unitrace_opt = []
unitrace_output, command = get_unitrace().setup(
self.name(), command, extra_unitrace_opt
)
log.debug(f"Unitrace output: {unitrace_output}")
log.debug(f"Unitrace command: {' '.join(command)}")

try:
result = run(
command=command,
env_vars=env_vars,
add_sycl=add_sycl,
cwd=options.benchmark_cwd,
ld_library=ld_libraries,
)
except subprocess.CalledProcessError:
if run_unitrace:
get_unitrace().cleanup(options.benchmark_cwd, unitrace_output)
raise

if self.traceable() and run_unitrace:
get_unitrace().handle_output(unitrace_output)

if use_stdout:
return result.stdout.decode()
Expand Down
4 changes: 3 additions & 1 deletion devops/scripts/benchmarks/benches/benchdnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def setup(self):
if not self.bench_bin.exists():
raise FileNotFoundError(f"Benchmark binary not found: {self.bench_bin}")

def run(self, env_vars):
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
command = [
str(self.bench_bin),
*self.bench_args.split(),
Expand All @@ -151,6 +151,8 @@ def run(self, env_vars):
add_sycl=True,
ld_library=ld_library,
use_stdout=True,
run_unitrace=run_unitrace,
extra_unitrace_opt=["--chrome-dnn-logging"],
)
result_value = self._extract_time(output)

Expand Down
1 change: 1 addition & 0 deletions devops/scripts/benchmarks/benches/benchdnn_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"graph",
"sdpa-plain-f16",
"--reset --dt=f16 --case=complex_fusion/mha/sdpa-plain-implicit-causal-mask-fp32-bs1.json",
False, # Do not run SYCL graph for this benchmark
],
[
"graph",
Expand Down
12 changes: 10 additions & 2 deletions devops/scripts/benchmarks/benches/compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ def enabled(self) -> bool:
# Check if the specific runtime is enabled (or no specific runtime required)
return self.runtime is None or self.runtime in self.enabled_runtimes()

def name(self):
"""Returns the name of the benchmark, can be overridden."""
return self.bench_name

def bin_args(self) -> list[str]:
return []

Expand All @@ -320,7 +324,7 @@ def explicit_group(self):
def description(self) -> str:
return ""

def run(self, env_vars) -> list[Result]:
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
command = [
f"{self.benchmark_bin}",
f"--test={self.test}",
Expand All @@ -331,7 +335,11 @@ def run(self, env_vars) -> list[Result]:
command += self.bin_args()
env_vars.update(self.extra_env_vars())

result = self.run_bench(command, env_vars)
result = self.run_bench(
command,
env_vars,
run_unitrace=run_unitrace,
)
parsed_results = self.parse_output(result)
ret = []
for label, median, stddev, unit in parsed_results:
Expand Down
43 changes: 25 additions & 18 deletions devops/scripts/benchmarks/benches/gromacs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from utils.result import Result
from utils.oneapi import get_oneapi
from utils.logger import log
from utils.unitrace import get_unitrace


class GromacsBench(Suite):
Expand Down Expand Up @@ -64,24 +65,29 @@ def setup(self) -> None:

self.oneapi = get_oneapi()

cmd_list = [
"cmake",
f"-S {self.gromacs_src}",
f"-B {self.gromacs_build_path}",
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_CXX_COMPILER=clang++",
"-DCMAKE_C_COMPILER=clang",
"-DGMX_GPU=SYCL",
"-DGMX_SYCL_ENABLE_GRAPHS=ON",
"-DGMX_SYCL_ENABLE_EXPERIMENTAL_SUBMIT_API=ON",
"-DGMX_FFT_LIBRARY=MKL",
"-DGMX_GPU_FFT_LIBRARY=MKL",
f"-DMKLROOT={self.oneapi.mkl_dir()}",
"-DGMX_GPU_NB_CLUSTER_SIZE=8",
"-DGMX_GPU_NB_NUM_CLUSTER_PER_CELL_X=1",
"-DGMX_OPENMP=OFF",
]

if options.unitrace:
cmd_list.append("-DGMX_USE_ITT=ON")

run(
[
"cmake",
f"-S {str(self.directory)}/gromacs-repo",
f"-B {self.gromacs_build_path}",
f"-DCMAKE_BUILD_TYPE=Release",
f"-DCMAKE_CXX_COMPILER=clang++",
f"-DCMAKE_C_COMPILER=clang",
f"-DGMX_GPU=SYCL",
f"-DGMX_SYCL_ENABLE_GRAPHS=ON",
f"-DGMX_SYCL_ENABLE_EXPERIMENTAL_SUBMIT_API=ON",
f"-DGMX_FFT_LIBRARY=MKL",
f"-DGMX_GPU_FFT_LIBRARY=MKL",
f"-DMKLROOT={self.oneapi.mkl_dir()}",
f"-DGMX_GPU_NB_CLUSTER_SIZE=8",
f"-DGMX_GPU_NB_NUM_CLUSTER_PER_CELL_X=1",
f"-DGMX_OPENMP=OFF",
],
cmd_list,
add_sycl=True,
)
run(
Expand Down Expand Up @@ -163,7 +169,7 @@ def setup(self):
ld_library=self.suite.oneapi.ld_libraries(),
)

def run(self, env_vars):
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
model_dir = self.grappa_dir / self.model

env_vars.update({"SYCL_CACHE_PERSISTENT": "1"})
Expand Down Expand Up @@ -202,6 +208,7 @@ def run(self, env_vars):
add_sycl=True,
use_stdout=False,
ld_library=self.suite.oneapi.ld_libraries(),
run_unitrace=run_unitrace,
)

if not self._validate_correctness(options.benchmark_cwd + "/md.log"):
Expand Down
7 changes: 5 additions & 2 deletions devops/scripts/benchmarks/benches/llamacpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def get_tags(self):
def lower_is_better(self):
return False

def run(self, env_vars) -> list[Result]:
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
command = [
f"{self.benchmark_bin}",
"--output",
Expand All @@ -141,7 +141,10 @@ def run(self, env_vars) -> list[Result]:
]

result = self.run_bench(
command, env_vars, ld_library=self.bench.oneapi.ld_libraries()
command,
env_vars,
ld_library=self.bench.oneapi.ld_libraries(),
run_unitrace=run_unitrace,
)
parsed = self.parse_output(result)
results = []
Expand Down
8 changes: 6 additions & 2 deletions devops/scripts/benchmarks/benches/syclbench.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def setup(self):
self.directory, "sycl-bench-build", self.bench_name
)

def run(self, env_vars) -> list[Result]:
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
self.outputfile = os.path.join(self.bench.directory, self.test + ".csv")

command = [
Expand All @@ -151,7 +151,11 @@ def run(self, env_vars) -> list[Result]:
env_vars.update(self.extra_env_vars())

# no output to stdout, all in outputfile
self.run_bench(command, env_vars)
self.run_bench(
command,
env_vars,
run_unitrace=run_unitrace,
)

with open(self.outputfile, "r") as f:
reader = csv.reader(f)
Expand Down
2 changes: 1 addition & 1 deletion devops/scripts/benchmarks/benches/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def notes(self) -> str:
def unstable(self) -> str:
return self.unstable_text

def run(self, env_vars) -> list[Result]:
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
random_value = self.value + random.uniform(-1 * (self.diff), self.diff)
return [
Result(
Expand Down
8 changes: 6 additions & 2 deletions devops/scripts/benchmarks/benches/umf.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def get_names_of_benchmarks_to_be_run(self, command, env_vars):

return all_names

def run(self, env_vars) -> list[Result]:
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
command = [f"{self.benchmark_bin}"]

all_names = self.get_names_of_benchmarks_to_be_run(command, env_vars)
Expand All @@ -151,7 +151,11 @@ def run(self, env_vars) -> list[Result]:
specific_benchmark = command + ["--benchmark_filter=^" + name + "$"]

result = self.run_bench(
specific_benchmark, env_vars, add_sycl=False, ld_library=[self.umf_lib]
specific_benchmark,
env_vars,
add_sycl=False,
ld_library=[self.umf_lib],
run_unitrace=run_unitrace,
)

parsed = self.parse_output(result)
Expand Down
11 changes: 8 additions & 3 deletions devops/scripts/benchmarks/benches/velocity.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,20 @@ def description(self) -> str:
def get_tags(self):
return ["SYCL", "application"]

def run(self, env_vars) -> list[Result]:
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
env_vars.update(self.extra_env_vars())

command = [
f"{self.benchmark_bin}",
]
command += self.bin_args()

result = self.run_bench(command, env_vars, ld_library=self.ld_libraries())
result = self.run_bench(
command,
env_vars,
ld_library=self.ld_libraries(),
run_unitrace=run_unitrace,
)

return [
Result(
Expand Down Expand Up @@ -282,7 +287,7 @@ class QuickSilver(VelocityBase):
def __init__(self, vb: VelocityBench):
super().__init__("QuickSilver", "qs", vb, "MMS/CTT")

def run(self, env_vars) -> list[Result]:
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
# TODO: fix the crash in QuickSilver when UR_L0_USE_IMMEDIATE_COMMANDLISTS=0
if (
"UR_L0_USE_IMMEDIATE_COMMANDLISTS" in env_vars
Expand Down
29 changes: 18 additions & 11 deletions devops/scripts/benchmarks/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
from utils.validate import Validate
from utils.logger import log
from utils.detect_versions import DetectVersion
from utils.unitrace import get_unitrace


class BenchmarkHistory:
runs = []
TIMESTAMP_FORMAT = "%Y%m%d_%H%M%S"

def __init__(self, dir):
self.dir = dir
Expand Down Expand Up @@ -78,7 +78,9 @@ def is_file_too_old(file_path: Path) -> bool:
if not timestamp_str:
return False

file_timestamp = datetime.strptime(timestamp_str, self.TIMESTAMP_FORMAT)
file_timestamp = datetime.strptime(
timestamp_str, options.TIMESTAMP_FORMAT
)
# Add timezone info for proper comparison
file_timestamp = file_timestamp.replace(tzinfo=timezone.utc)
return file_timestamp < cutoff_date
Expand Down Expand Up @@ -190,23 +192,28 @@ def git_info_from_path(path: Path) -> (str, str):
compute_runtime=compute_runtime,
)

def save(self, save_name, results: list[Result], to_file=True):
def save(self, save_name, results: list[Result]):
benchmark_data = self.create_run(save_name, results)
self.runs.append(benchmark_data)

if not to_file:
if options.save_name is None:
return

serialized = benchmark_data.to_json()
serialized = benchmark_data.to_json() # type: ignore
results_dir = Path(os.path.join(self.dir, "results"))
os.makedirs(results_dir, exist_ok=True)

# Use formatted timestamp for the filename
timestamp = (
datetime.now(tz=timezone.utc).strftime(self.TIMESTAMP_FORMAT)
if options.timestamp_override is None
else options.timestamp_override
)
if options.unitrace:
timestamp = get_unitrace().timestamp # type: ignore
elif options.timestamp_override is not None:
timestamp = options.timestamp_override
else:
timestamp = (
datetime.now(tz=timezone.utc).strftime(self.TIMESTAMP_FORMAT)
if options.timestamp_override is None
else options.timestamp_override
)

file_path = Path(os.path.join(results_dir, f"{save_name}_{timestamp}.json"))
with file_path.open("w") as file:
json.dump(serialized, file, indent=4)
Expand Down
Loading