diff --git a/devops/scripts/benchmarks/benches/base.py b/devops/scripts/benchmarks/benches/base.py
index 4c2973d250e3d..ab38df2e74711 100644
--- a/devops/scripts/benchmarks/benches/base.py
+++ b/devops/scripts/benchmarks/benches/base.py
@@ -129,13 +129,20 @@ def unstable(self) -> str:
def get_tags(self) -> list[str]:
return []
+ def range(self) -> tuple[float, float]:
+ return None
+
def get_metadata(self) -> BenchmarkMetadata:
+ range = self.range()
+
return BenchmarkMetadata(
type="benchmark",
description=self.description(),
notes=self.notes(),
unstable=self.unstable(),
tags=self.get_tags(),
+ range_min=range[0] if range else None,
+ range_max=range[1] if range else None,
)
diff --git a/devops/scripts/benchmarks/benches/compute.py b/devops/scripts/benchmarks/benches/compute.py
index b014828b4f6b8..0468c5ee1d25b 100644
--- a/devops/scripts/benchmarks/benches/compute.py
+++ b/devops/scripts/benchmarks/benches/compute.py
@@ -92,6 +92,7 @@ def additionalMetadata(self) -> dict[str, BenchmarkMetadata]:
"The UR v2 adapter noticeably reduces UR layer overhead, also improving SYCL performance.\n"
"Work is ongoing to reduce the overhead of the SYCL API\n",
tags=["submit", "micro", "SYCL", "UR", "L0"],
+ range_min=0.0,
),
"SinKernelGraph": BenchmarkMetadata(
type="group",
@@ -317,6 +318,9 @@ def description(self) -> str:
f"Runs 10 simple kernels with minimal execution time to isolate API overhead from kernel execution time. {l0_specific}"
)
+ def range(self) -> tuple[float, float]:
+ return (0.0, None)
+
def bin_args(self) -> list[str]:
return [
f"--Ioq={self.ioq}",
diff --git a/devops/scripts/benchmarks/html/index.html b/devops/scripts/benchmarks/html/index.html
index 81c82c72fe471..541bd744028e6 100644
--- a/devops/scripts/benchmarks/html/index.html
+++ b/devops/scripts/benchmarks/html/index.html
@@ -47,6 +47,10 @@
Display Options
Show unstable scenarios
+
diff --git a/devops/scripts/benchmarks/html/scripts.js b/devops/scripts/benchmarks/html/scripts.js
index 6cd1857387024..77ce11afc7caa 100644
--- a/devops/scripts/benchmarks/html/scripts.js
+++ b/devops/scripts/benchmarks/html/scripts.js
@@ -97,7 +97,7 @@ function createChart(data, containerId, type) {
];
} else {
return [`${context.dataset.label}:`,
- `Value: ${context.parsed.y.toFixed(2)} ${data.unit}`,
+ `Value: ${context.parsed.y.toFixed(2)} ${data.unit}`,
];
}
}
@@ -111,6 +111,8 @@ function createChart(data, containerId, type) {
text: data.unit
},
grace: '20%',
+ min: isCustomRangesEnabled() ? data.range_min : null,
+ max: isCustomRangesEnabled() ? data.range_max : null
}
}
};
@@ -182,7 +184,7 @@ function updateCharts() {
const filterRunData = (chart) => ({
...chart,
runs: Object.fromEntries(
- Object.entries(chart.runs).filter(([_, data]) =>
+ Object.entries(chart.runs).filter(([_, data]) =>
activeRuns.has(data.runName)
)
)
@@ -276,25 +278,25 @@ function createChartContainer(data, canvasId, type) {
// Add tags if present
if (metadata && metadata.tags) {
container.setAttribute('data-tags', metadata.tags.join(','));
-
+
// Add tags display
const tagsContainer = document.createElement('div');
tagsContainer.className = 'benchmark-tags';
-
+
metadata.tags.forEach(tag => {
const tagElement = document.createElement('span');
tagElement.className = 'tag';
tagElement.textContent = tag;
tagElement.setAttribute('data-tag', tag);
-
+
// Add tooltip with tag description
if (benchmarkTags[tag]) {
tagElement.setAttribute('title', benchmarkTags[tag].description);
}
-
+
tagsContainer.appendChild(tagElement);
});
-
+
container.appendChild(tagsContainer);
}
@@ -493,6 +495,12 @@ function updateURL() {
url.searchParams.set('unstable', 'true');
}
+ if (!isCustomRangesEnabled()) {
+ url.searchParams.delete('customRange');
+ } else {
+ url.searchParams.set('customRange', 'true');
+ }
+
history.replaceState(null, '', url);
}
@@ -505,12 +513,12 @@ function filterCharts() {
const label = container.getAttribute('data-label');
const suite = container.getAttribute('data-suite');
const isUnstable = container.getAttribute('data-unstable') === 'true';
- const tags = container.getAttribute('data-tags') ?
- container.getAttribute('data-tags').split(',') : [];
+ const tags = container.getAttribute('data-tags') ?
+ container.getAttribute('data-tags').split(',') : [];
// Check if benchmark has all active tags (if any are selected)
- const hasAllActiveTags = activeTags.size === 0 ||
- Array.from(activeTags).every(tag => tags.includes(tag));
+ const hasAllActiveTags = activeTags.size === 0 ||
+ Array.from(activeTags).every(tag => tags.includes(tag));
// Hide unstable benchmarks if showUnstable is false
const shouldShow = regex.test(label) &&
@@ -535,16 +543,19 @@ function processTimeseriesData(benchmarkRuns) {
benchmarkRuns.forEach(run => {
run.results.forEach(result => {
+ const metadata = metadataForLabel(result.label, 'benchmark');
+
if (!resultsByLabel[result.label]) {
resultsByLabel[result.label] = {
label: result.label,
suite: result.suite,
unit: result.unit,
lower_is_better: result.lower_is_better,
+ range_min: metadata?.range_min ?? null, // can't use || because js treats 0 as null
+ range_max: metadata?.range_max ?? null,
runs: {}
};
}
-
addRunDataPoint(resultsByLabel[result.label], run, result, run.name);
});
});
@@ -568,6 +579,8 @@ function processBarChartsData(benchmarkRuns) {
suite: result.suite,
unit: result.unit,
lower_is_better: result.lower_is_better,
+ range_min: groupMetadata?.range_min ?? null, // can't use || because js treats 0 as null
+ range_max: groupMetadata?.range_max ?? null,
labels: [],
datasets: [],
// Add metadata if available
@@ -654,6 +667,8 @@ function processLayerComparisonsData(benchmarkRuns) {
suite: result.suite,
unit: result.unit,
lower_is_better: result.lower_is_better,
+ range_min: metadata?.range_min ?? null, // can't use || because js treats 0 as null
+ range_max: metadata?.range_max ?? null,
runs: {},
benchmarkLabels: [],
description: metadata?.description || null,
@@ -760,11 +775,17 @@ function isUnstableEnabled() {
return unstableToggle.checked;
}
+function isCustomRangesEnabled() {
+ const rangesToggle = document.getElementById('custom-range');
+ return rangesToggle.checked;
+}
+
function setupToggles() {
const notesToggle = document.getElementById('show-notes');
const unstableToggle = document.getElementById('show-unstable');
+ const customRangeToggle = document.getElementById('custom-range');
- notesToggle.addEventListener('change', function() {
+ notesToggle.addEventListener('change', function () {
// Update all note elements visibility
document.querySelectorAll('.benchmark-note').forEach(note => {
note.style.display = isNotesEnabled() ? 'block' : 'none';
@@ -772,7 +793,7 @@ function setupToggles() {
updateURL();
});
- unstableToggle.addEventListener('change', function() {
+ unstableToggle.addEventListener('change', function () {
// Update all unstable warning elements visibility
document.querySelectorAll('.benchmark-unstable').forEach(warning => {
warning.style.display = isUnstableEnabled() ? 'block' : 'none';
@@ -780,6 +801,11 @@ function setupToggles() {
filterCharts();
});
+ customRangeToggle.addEventListener('change', function () {
+ // redraw all charts
+ updateCharts();
+ });
+
// Initialize from URL params if present
const notesParam = getQueryParam('notes');
const unstableParam = getQueryParam('unstable');
@@ -793,13 +819,18 @@ function setupToggles() {
let showUnstable = unstableParam === 'true';
unstableToggle.checked = showUnstable;
}
+
+ const customRangesParam = getQueryParam('customRange');
+ if (customRangesParam !== null) {
+ customRangeToggle.checked = customRangesParam === 'true';
+ }
}
function setupTagFilters() {
tagFiltersContainer = document.getElementById('tag-filters');
const allTags = [];
-
+
if (benchmarkTags) {
for (const tag in benchmarkTags) {
if (!allTags.includes(tag)) {
@@ -812,17 +843,17 @@ function setupTagFilters() {
allTags.forEach(tag => {
const tagContainer = document.createElement('div');
tagContainer.className = 'tag-filter';
-
+
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `tag-${tag}`;
checkbox.className = 'tag-checkbox';
checkbox.dataset.tag = tag;
-
+
const label = document.createElement('label');
label.htmlFor = `tag-${tag}`;
label.textContent = tag;
-
+
// Add info icon with tooltip if tag description exists
if (benchmarkTags[tag]) {
const infoIcon = document.createElement('span');
@@ -831,8 +862,8 @@ function setupTagFilters() {
infoIcon.title = benchmarkTags[tag].description;
label.appendChild(infoIcon);
}
-
- checkbox.addEventListener('change', function() {
+
+ checkbox.addEventListener('change', function () {
if (this.checked) {
activeTags.add(tag);
} else {
@@ -840,7 +871,7 @@ function setupTagFilters() {
}
filterCharts();
});
-
+
tagContainer.appendChild(checkbox);
tagContainer.appendChild(label);
tagFiltersContainer.appendChild(tagContainer);
@@ -849,18 +880,18 @@ function setupTagFilters() {
function toggleAllTags(select) {
const checkboxes = document.querySelectorAll('.tag-checkbox');
-
+
checkboxes.forEach(checkbox => {
checkbox.checked = select;
const tag = checkbox.dataset.tag;
-
+
if (select) {
activeTags.add(tag);
} else {
activeTags.delete(tag);
}
});
-
+
filterCharts();
}
diff --git a/devops/scripts/benchmarks/utils/result.py b/devops/scripts/benchmarks/utils/result.py
index 6afcc8d1b627b..c3dd82b08ae70 100644
--- a/devops/scripts/benchmarks/utils/result.py
+++ b/devops/scripts/benchmarks/utils/result.py
@@ -59,6 +59,8 @@ class BenchmarkMetadata:
notes: str = None
unstable: str = None
tags: list[str] = field(default_factory=list)
+ range_min: float = None
+ range_max: float = None
@dataclass_json