Skip to content

Commit 48e397c

Browse files
authored
[UR][Benchmarks] Add flamegraphs to benchmark results (#19678)
Adds presentation of perf results as flamegraphs Run main.py with: --flamegraph [exclusive] to create flamegraphs --flamegraph force to create flamegraphs also for benchmarks marked as non-traceable --flamegraph inclusive to create flamegraphs along with regular benchmarks (the same options work for unitrace logs, with --unitrace) Number of internal iterations in ComputeRuntime benchmark is reduced when generatin traces/flamegraphs --------- Signed-off-by: Mateusz P. Nowak <[email protected]>
1 parent 5e94b2d commit 48e397c

File tree

19 files changed

+1452
-209
lines changed

19 files changed

+1452
-209
lines changed

devops/scripts/benchmarks/benches/base.py

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,24 @@
77
import shutil
88
import subprocess
99
from pathlib import Path
10+
from enum import Enum
1011
from utils.result import BenchmarkMetadata, BenchmarkTag, Result
1112
from options import options
1213
from utils.utils import download, run
1314
from abc import ABC, abstractmethod
1415
from utils.unitrace import get_unitrace
16+
from utils.flamegraph import get_flamegraph
1517
from utils.logger import log
1618

19+
20+
class TracingType(Enum):
21+
"""Enumeration of available tracing types."""
22+
23+
NONE = ""
24+
UNITRACE = "unitrace"
25+
FLAMEGRAPH = "flamegraph"
26+
27+
1728
benchmark_tags = [
1829
BenchmarkTag("SYCL", "Benchmark uses SYCL runtime"),
1930
BenchmarkTag("UR", "Benchmark uses Unified Runtime API"),
@@ -62,12 +73,17 @@ def enabled(self) -> bool:
6273
By default, it returns True, but can be overridden to disable a benchmark."""
6374
return True
6475

65-
def traceable(self) -> bool:
66-
"""Returns whether this benchmark should be traced by Unitrace.
67-
By default, it returns True, but can be overridden to disable tracing for a benchmark.
76+
def traceable(self, tracing_type: TracingType) -> bool:
77+
"""Returns whether this benchmark should be traced by the specified tracing method.
78+
By default, it returns True for all tracing types, but can be overridden
79+
to disable specific tracing methods for a benchmark.
6880
"""
6981
return True
7082

83+
def tracing_enabled(self, run_trace, force_trace, tr_type: TracingType):
84+
"""Returns whether tracing is enabled for the given type."""
85+
return (self.traceable(tr_type) or force_trace) and run_trace == tr_type
86+
7187
@abstractmethod
7288
def setup(self):
7389
pass
@@ -77,12 +93,18 @@ def teardown(self):
7793
pass
7894

7995
@abstractmethod
80-
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
96+
def run(
97+
self,
98+
env_vars,
99+
run_trace: TracingType = TracingType.NONE,
100+
force_trace: bool = False,
101+
) -> list[Result]:
81102
"""Execute the benchmark with the given environment variables.
82103
83104
Args:
84105
env_vars: Environment variables to use when running the benchmark.
85-
run_unitrace: Whether to run benchmark under Unitrace.
106+
run_trace: The type of tracing to run (NONE, UNITRACE, or FLAMEGRAPH).
107+
force_trace: If True, ignore the traceable() method and force tracing.
86108
87109
Returns:
88110
A list of Result objects with the benchmark results.
@@ -111,8 +133,9 @@ def run_bench(
111133
ld_library=[],
112134
add_sycl=True,
113135
use_stdout=True,
114-
run_unitrace=False,
115-
extra_unitrace_opt=None,
136+
run_trace: TracingType = TracingType.NONE,
137+
extra_trace_opt=None,
138+
force_trace: bool = False,
116139
):
117140
env_vars = env_vars.copy()
118141
if options.ur is not None:
@@ -125,15 +148,26 @@ def run_bench(
125148
ld_libraries = options.extra_ld_libraries.copy()
126149
ld_libraries.extend(ld_library)
127150

128-
if self.traceable() and run_unitrace:
129-
if extra_unitrace_opt is None:
130-
extra_unitrace_opt = []
151+
unitrace_output = None
152+
if self.tracing_enabled(run_trace, force_trace, TracingType.UNITRACE):
153+
if extra_trace_opt is None:
154+
extra_trace_opt = []
131155
unitrace_output, command = get_unitrace().setup(
132-
self.name(), command, extra_unitrace_opt
156+
self.name(), command, extra_trace_opt
133157
)
134158
log.debug(f"Unitrace output: {unitrace_output}")
135159
log.debug(f"Unitrace command: {' '.join(command)}")
136160

161+
# flamegraph run
162+
163+
perf_data_file = None
164+
if self.tracing_enabled(run_trace, force_trace, TracingType.FLAMEGRAPH):
165+
perf_data_file, command = get_flamegraph().setup(
166+
self.name(), self.get_suite_name(), command
167+
)
168+
log.debug(f"FlameGraph perf data: {perf_data_file}")
169+
log.debug(f"FlameGraph command: {' '.join(command)}")
170+
137171
try:
138172
result = run(
139173
command=command,
@@ -143,13 +177,27 @@ def run_bench(
143177
ld_library=ld_libraries,
144178
)
145179
except subprocess.CalledProcessError:
146-
if run_unitrace:
180+
if run_trace == TracingType.UNITRACE and unitrace_output:
147181
get_unitrace().cleanup(options.benchmark_cwd, unitrace_output)
182+
if run_trace == TracingType.FLAMEGRAPH and perf_data_file:
183+
get_flamegraph().cleanup(perf_data_file)
148184
raise
149185

150-
if self.traceable() and run_unitrace:
186+
if (
187+
self.tracing_enabled(run_trace, force_trace, TracingType.UNITRACE)
188+
and unitrace_output
189+
):
151190
get_unitrace().handle_output(unitrace_output)
152191

192+
if (
193+
self.tracing_enabled(run_trace, force_trace, TracingType.FLAMEGRAPH)
194+
and perf_data_file
195+
):
196+
svg_file = get_flamegraph().handle_output(
197+
self.name(), perf_data_file, self.get_suite_name()
198+
)
199+
log.info(f"FlameGraph generated: {svg_file}")
200+
153201
if use_stdout:
154202
return result.stdout.decode()
155203
else:

devops/scripts/benchmarks/benches/benchdnn.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from pathlib import Path
88

9-
from .base import Suite, Benchmark
9+
from .base import Suite, Benchmark, TracingType
1010
from options import options
1111
from utils.utils import git_clone, run, create_build_path
1212
from utils.result import Result
@@ -132,7 +132,18 @@ def setup(self):
132132
if not self.bench_bin.exists():
133133
raise FileNotFoundError(f"Benchmark binary not found: {self.bench_bin}")
134134

135-
def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
135+
def run(
136+
self,
137+
env_vars,
138+
run_trace: TracingType = TracingType.NONE,
139+
force_trace: bool = False,
140+
) -> list[Result]:
141+
# Determine extra trace options based on tracing type
142+
if run_trace == TracingType.UNITRACE:
143+
extra_trace_opt = ["--chrome-dnn-logging"]
144+
else:
145+
extra_trace_opt = None
146+
136147
command = [
137148
str(self.bench_bin),
138149
*self.bench_args.split(),
@@ -151,8 +162,9 @@ def run(self, env_vars, run_unitrace: bool = False) -> list[Result]:
151162
add_sycl=True,
152163
ld_library=ld_library,
153164
use_stdout=True,
154-
run_unitrace=run_unitrace,
155-
extra_unitrace_opt=["--chrome-dnn-logging"],
165+
run_trace=run_trace,
166+
extra_trace_opt=extra_trace_opt,
167+
force_trace=force_trace,
156168
)
157169
result_value = self._extract_time(output)
158170

0 commit comments

Comments
 (0)