Skip to content

Commit b0f6da7

Browse files
authored
[CI][Benchmarks] Update of benchmark dashboard - flamegraphs (#20016)
Flamegraphs generation added to CI action and links to flamegraphs (if they exist for given benchmark) added to the dashboard. --------- Signed-off-by: Mateusz P. Nowak <[email protected]>
1 parent e3028dc commit b0f6da7

File tree

8 files changed

+561
-217
lines changed

8 files changed

+561
-217
lines changed

devops/actions/run-tests/benchmark/action.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ runs:
203203
--output-dir "./llvm-ci-perf-results/" \
204204
--preset "$PRESET" \
205205
--timestamp-override "$SAVE_TIMESTAMP" \
206-
--detect-version sycl,compute_runtime
206+
--detect-version sycl,compute_runtime \
207+
--flamegraph inclusive
207208

208209
echo "-----"
209210
python3 ./devops/scripts/benchmarks/compare.py to_hist \

devops/scripts/benchmarks/html/scripts.js

Lines changed: 324 additions & 142 deletions
Large diffs are not rendered by default.

devops/scripts/benchmarks/html/styles.css

Lines changed: 153 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
======================================== */
44
:root {
55
/* Core Color Palette - only used colors from scripts.js */
6-
--color-red: rgb(255, 50, 80);
7-
--color-orange: rgb(255, 145, 15);
6+
/* Provide RGB component vars to allow reuse in rgba() backgrounds */
7+
--color-red-rgb: 255, 50, 80;
8+
--color-orange-rgb: 255, 145, 15;
9+
--color-red: rgb(var(--color-red-rgb));
10+
--color-orange: rgb(var(--color-orange-rgb));
811
--color-yellow: rgb(255, 220, 0);
912
--color-green: rgb(20, 200, 50);
1013
--color-blue: rgb(0, 130, 255);
@@ -24,12 +27,14 @@
2427
/* Backgrounds - consolidated similar grays */
2528
--bg-body: #f8f9fa; /* Replaces bg-lighter, bg-light-gray */
2629
--bg-white: white;
30+
--bg-hover: #f5f5f5; /* used for hover states (was fallback) */
2731
--bg-light: #e9ecef; /* Replaces bg-disabled */
2832
--bg-summary: #dee2e6;
2933
--bg-summary-hover: #ced4da;
3034
--bg-info: #cfe2ff;
31-
--bg-warning: rgba(255, 145, 15, 0.1); /* Light orange background for warnings */
32-
--bg-danger: rgba(255, 50, 80, 0.1); /* Light red background for errors */
35+
--bg-warning: rgba(var(--color-orange-rgb), 0.1); /* Light orange background for warnings */
36+
--bg-danger: rgba(var(--color-red-rgb), 0.1); /* Light red background for errors */
37+
background: var(--bg-white);
3338

3439
/* Borders - simplified */
3540
--border-light: #ccc;
@@ -57,10 +62,10 @@ h1, h2 {
5762
font-weight: 500;
5863
}
5964
.chart-container {
60-
background: white;
61-
border-radius: 8px;
65+
/* Removed direct literal 'white'; using themed hover background */
66+
background: var(--bg-hover);
6267
padding: 24px;
63-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
68+
box-shadow: var(--shadow-subtle);
6469
position: relative;
6570
display: flex;
6671
flex-direction: column;
@@ -168,7 +173,7 @@ details[open] summary::after {
168173
.run-selector button {
169174
padding: 8px 16px;
170175
background: var(--color-blue);
171-
color: white;
176+
color: var(--bg-white);
172177
border: none;
173178
border-radius: 4px;
174179
cursor: pointer;
@@ -206,6 +211,51 @@ details[open] summary::after {
206211
.download-button:hover {
207212
color: var(--color-cyan);
208213
}
214+
215+
.download-button:disabled {
216+
color: var(--text-muted);
217+
cursor: not-allowed;
218+
}
219+
220+
.download-list {
221+
position: absolute;
222+
z-index: 1000;
223+
background: var(--bg-white);
224+
border: 1px solid var(--border-medium);
225+
border-radius: 4px;
226+
padding: 4px;
227+
min-width: 200px;
228+
max-width: 300px;
229+
box-shadow: var(--shadow-dropdown);
230+
font-size: 0.9rem;
231+
}
232+
233+
/* Support both legacy .download-list-link and generic anchors inside list */
234+
.download-list a,
235+
.download-list-link {
236+
display: block;
237+
padding: 8px 12px;
238+
text-decoration: none;
239+
color: var(--text-dark);
240+
border-radius: 2px;
241+
font-size: 14px;
242+
line-height: 1.2;
243+
white-space: nowrap;
244+
overflow: hidden;
245+
text-overflow: ellipsis;
246+
}
247+
248+
.download-list a:hover,
249+
.download-list a:focus,
250+
.download-list-link:hover,
251+
.download-list-link:focus {
252+
background: var(--bg-light);
253+
outline: none;
254+
}
255+
256+
.chart-download-button {
257+
margin-right: 8px;
258+
}
209259
.loading-indicator {
210260
text-align: center;
211261
font-size: 18px;
@@ -369,6 +419,48 @@ details[open] summary::after {
369419
cursor: help;
370420
font-size: 12px;
371421
}
422+
423+
/* Flamegraph link area styles (used by scripts.js) */
424+
.chart-flamegraph-links {
425+
display: flex;
426+
align-items: center;
427+
gap: 12px;
428+
margin-top: 8px;
429+
/* small margin below links to separate from any gray bar or footer */
430+
margin-bottom: 6px;
431+
}
432+
.flamegraph-label {
433+
color: var(--text-dark);
434+
font-weight: 600;
435+
}
436+
.flamegraph-links-inline {
437+
display: flex;
438+
gap: 8px;
439+
flex-wrap: wrap;
440+
}
441+
.flamegraph-link {
442+
color: var(--text-warning);
443+
text-decoration: none;
444+
font-weight: 500;
445+
display: inline-flex;
446+
align-items: center;
447+
gap: 6px;
448+
}
449+
.flamegraph-link:hover {
450+
text-decoration: underline;
451+
}
452+
.flame-icon {
453+
font-size: 16px;
454+
line-height: 1;
455+
display: inline-block;
456+
}
457+
.flame-text {
458+
white-space: nowrap;
459+
overflow: hidden;
460+
text-overflow: ellipsis;
461+
max-width: 220px;
462+
display: inline-block;
463+
}
372464
#tag-filters {
373465
display: flex;
374466
flex-wrap: wrap;
@@ -395,7 +487,7 @@ details[open] summary::after {
395487
.remove-tag {
396488
background: none;
397489
border: none;
398-
color: white;
490+
color: var(--bg-white);
399491
margin-left: 4px;
400492
cursor: pointer;
401493
font-size: 16px;
@@ -406,7 +498,7 @@ details[open] summary::after {
406498
}
407499
.platform {
408500
padding: 16px;
409-
background: white;
501+
background: var(--bg-white);
410502
border-radius: 8px;
411503
margin-top: 8px;
412504
}
@@ -496,9 +588,10 @@ details[open] summary::after {
496588
}
497589

498590
.flamegraph-error {
499-
background-color: var(--bg-warning);
500-
border-color: var(--color-orange);
501-
color: var(--text-warning);
591+
/* Repurposed to use dedicated danger background */
592+
background-color: var(--bg-danger);
593+
border-color: var(--text-danger);
594+
color: var(--text-danger);
502595
}
503596

504597
/* ========================================
@@ -530,30 +623,63 @@ details[open] summary::after {
530623
border: 1px solid var(--border-medium);
531624
border-radius: 4px;
532625
display: block;
533-
margin: 10px auto;
626+
margin: 0 0 10px 0;
534627
transition: all 0.3s ease;
535628
box-sizing: border-box;
536629
overflow: hidden;
630+
/* Ensure maximum width utilization */
631+
max-width: none;
632+
min-width: 0;
537633
}
538634

539-
.flamegraph-iframe:first-child {
540-
margin: 0 auto 10px auto;
635+
/* Flamegraph container styles - gives each flamegraph its own space */
636+
.flamegraph-container {
637+
margin-bottom: 20px;
638+
padding: 0;
639+
border: none;
640+
border-radius: 0;
641+
background-color: transparent;
642+
width: 100%;
643+
/* Ensure no width constraints */
644+
max-width: none;
645+
min-width: 0;
646+
box-sizing: border-box;
647+
}
648+
649+
.flamegraph-container:last-child {
650+
margin-bottom: 0;
651+
}
652+
653+
.flamegraph-title {
654+
font-size: 16px;
655+
font-weight: 600;
656+
color: var(--text-dark);
657+
margin: 0 0 10px 0;
658+
padding: 8px 12px;
659+
background-color: var(--bg-light);
660+
border-radius: 4px;
661+
border-left: 4px solid var(--color-blue);
541662
}
542663

543664
/* Ensure flamegraph containers have proper spacing and fit within container */
544-
.chart-container iframe {
545-
margin-bottom: 10px;
665+
.chart-container .flamegraph-container {
666+
margin-bottom: 20px;
667+
width: 100%;
546668
}
547669

548-
/* Handle multiple flamegraphs displayed vertically */
549-
.chart-content iframe[src*="flamegraphs"]:not(:last-child) {
550-
margin-bottom: 15px;
551-
border-bottom: 2px solid var(--bg-light);
670+
/* Reduce padding for chart containers that contain flamegraphs to maximize space */
671+
.chart-container.flamegraph-chart {
672+
padding: 8px;
673+
}
674+
675+
/* Ensure chart content doesn't constrain flamegraphs */
676+
.chart-container.flamegraph-chart .chart-content {
677+
padding: 0;
552678
}
553679

554-
/* Add subtle visual separation between multiple flamegraphs */
555-
.chart-content iframe[src*="flamegraphs"]:not(:first-child) {
556-
margin-top: 15px;
680+
/* Handle multiple flamegraphs displayed vertically - now handled by container */
681+
.flamegraph-container:not(:last-child) {
682+
margin-bottom: 20px;
557683
}
558684

559685
/* Floating flamegraph download list */
@@ -562,8 +688,8 @@ details[open] summary::after {
562688
z-index: 1000;
563689
border: 1px solid var(--border-light);
564690
border-radius: 4px;
565-
background-color: white;
566-
box-shadow: 0 2px 5px rgba(0,0,0,0.15);
691+
background-color: var(--bg-white);
692+
box-shadow: var(--shadow-dropdown);
567693
padding: 5px;
568694
margin-top: 5px;
569695
}

devops/scripts/benchmarks/output_html.py

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -70,53 +70,54 @@ def _write_output_to_file(
7070
"""
7171
# Define variable configuration based on whether we're archiving or not
7272
filename = "data_archive" if archive else "data"
73+
output_data = json.loads(output.to_json()) # type: ignore
74+
75+
if options.flamegraph:
76+
flamegraph_data = _get_flamegraph_data(html_path)
77+
if flamegraph_data and flamegraph_data.get("runs"):
78+
output_data["flamegraphData"] = flamegraph_data
79+
log.debug(
80+
f"Added flamegraph data for {len(flamegraph_data['runs'])} runs to {filename}.*"
81+
)
82+
83+
runs_list = output_data.get("runs", [])
7384

7485
if options.output_html == "local":
86+
# Local JS: emit standalone globals (legacy-style) without wrapper object
7587
data_path = os.path.join(html_path, f"{filename}.js")
7688
with open(data_path, "w") as f:
77-
# For local format, we need to write JavaScript variable assignments
7889
f.write("benchmarkRuns = ")
79-
json.dump(json.loads(output.to_json())["runs"], f, indent=2) # type: ignore
80-
f.write(";\n\n")
81-
82-
f.write(f"benchmarkMetadata = ")
83-
json.dump(json.loads(output.to_json())["metadata"], f, indent=2) # type: ignore
84-
f.write(";\n\n")
85-
86-
f.write(f"benchmarkTags = ")
87-
json.dump(json.loads(output.to_json())["tags"], f, indent=2) # type: ignore
88-
f.write(";\n\n")
89-
90-
f.write(f"defaultCompareNames = ")
91-
json.dump(output.default_compare_names, f, indent=2)
92-
f.write(";\n\n")
93-
94-
# Add flamegraph data if it exists
95-
if options.flamegraph:
96-
flamegraph_data = _get_flamegraph_data(html_path)
97-
if flamegraph_data and flamegraph_data.get("runs"):
98-
f.write("flamegraphData = ")
99-
json.dump(flamegraph_data, f, indent=2)
100-
f.write(";\n\n")
101-
log.debug(
102-
f"Added flamegraph data for {len(flamegraph_data['runs'])} runs to data.js"
103-
)
104-
105-
if not archive:
106-
log.info(f"See {html_path}/index.html for the results.")
90+
json.dump(runs_list, f, indent=2)
91+
f.write(";\n")
92+
if "flamegraphData" in output_data:
93+
f.write("flamegraphData = ")
94+
json.dump(output_data["flamegraphData"], f, indent=2)
95+
f.write(";\n")
96+
else:
97+
f.write("flamegraphData = { runs: {} };\n")
98+
f.write("benchmarkMetadata = ")
99+
json.dump(output_data.get("metadata", {}), f, indent=2)
100+
f.write(";\n")
101+
f.write("benchmarkTags = ")
102+
json.dump(output_data.get("tags", {}), f, indent=2)
103+
f.write(";\n")
104+
f.write("defaultCompareNames = ")
105+
json.dump(output.default_compare_names, f)
106+
f.write(";\n")
107+
if not archive:
108+
log.info(f"See {html_path}/index.html for the results.")
107109
else:
108-
# For remote format, we write a single JSON file
110+
# Remote JSON: emit flat schema aligning with local globals
111+
remote_obj = {
112+
"benchmarkRuns": runs_list,
113+
"benchmarkMetadata": output_data.get("metadata", {}),
114+
"benchmarkTags": output_data.get("tags", {}),
115+
"flamegraphData": output_data.get("flamegraphData", {"runs": {}}),
116+
"defaultCompareNames": output.default_compare_names,
117+
}
109118
data_path = os.path.join(html_path, f"{filename}.json")
110-
output_data = json.loads(output.to_json()) # type: ignore
111-
if options.flamegraph:
112-
flamegraph_data = _get_flamegraph_data(html_path)
113-
if flamegraph_data and flamegraph_data.get("runs"):
114-
output_data["flamegraphs"] = flamegraph_data
115-
log.debug(
116-
f"Added flamegraph data for {len(flamegraph_data['runs'])} runs to {filename}.json"
117-
)
118119
with open(data_path, "w") as f:
119-
json.dump(output_data, f, indent=2)
120+
json.dump(remote_obj, f, indent=2)
120121
log.info(
121122
f"Upload {data_path} to a location set in config.js remoteDataUrl argument."
122123
)

0 commit comments

Comments
 (0)