Skip to content

Commit d68f883

Browse files
committed
[Benchmarks] Archive old data
Archive old benchmark data so it is not loaded in the dashboard. The default value for archiving is: - for Baseline_* runs for data older than 90 days, - for all other runs for data older than 14 days. Add a UI option for loading archived data.
1 parent 8b445d6 commit d68f883

File tree

6 files changed

+273
-56
lines changed

6 files changed

+273
-56
lines changed

devops/scripts/benchmarks/history.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import socket
1010
from utils.result import Result, BenchmarkRun
1111
from options import Compare, options
12-
from datetime import datetime, timezone
12+
from datetime import datetime, timezone, timedelta
1313
from utils.utils import run
1414
from utils.validate import Validate
1515

@@ -223,3 +223,27 @@ def get_compare(self, name: str) -> BenchmarkRun:
223223
return self.compute_average(data)
224224

225225
raise Exception("invalid compare type")
226+
227+
def partition_runs_by_age(self) -> tuple[list[BenchmarkRun], list[BenchmarkRun]]:
228+
"""
229+
Partition runs into current and archived based on their age.
230+
Returns:
231+
tuple: (current_runs, archived_runs)
232+
"""
233+
current_runs = []
234+
archived_runs = []
235+
236+
for run in self.runs:
237+
archive_after = (
238+
options.archive_baseline_days
239+
if run.name.startswith("Baseline_")
240+
else options.archive_pr_days
241+
)
242+
cutoff_date = datetime.now(timezone.utc) - timedelta(days=archive_after)
243+
244+
if run.date > cutoff_date:
245+
current_runs.append(run)
246+
else:
247+
archived_runs.append(run)
248+
249+
return current_runs, archived_runs

devops/scripts/benchmarks/html/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ <h3>Display Options</h3>
5353
<input type="checkbox" id="custom-range">
5454
Adjust Y-axis for comparisons
5555
</label>
56+
<label title="Load older benchmark results that have been archived.">
57+
<input type="checkbox" id="show-archived-data">
58+
Include archived runs
59+
</label>
5660
</div>
5761
</div>
5862

devops/scripts/benchmarks/html/scripts.js

Lines changed: 159 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,22 @@
77
let activeRuns = new Set(defaultCompareNames);
88
let chartInstances = new Map();
99
let suiteNames = new Set();
10-
let timeseriesData, barChartsData, allRunNames;
1110
let activeTags = new Set();
11+
let timeseriesData, barChartsData, allRunNames;
1212
let layerComparisonsData;
1313
let latestRunsLookup = new Map();
1414
let pendingCharts = new Map(); // Store chart data for lazy loading
1515
let chartObserver; // Intersection observer for lazy loading charts
1616
let annotationsOptions = new Map(); // Global options map for annotations
17+
let archivedDataLoaded = false;
1718

1819
// DOM Elements
1920
let runSelect, selectedRunsDiv, suiteFiltersContainer, tagFiltersContainer;
2021

2122
// Observer for lazy loading charts
2223
function initChartObserver() {
2324
if (chartObserver) return;
24-
25+
2526
chartObserver = new IntersectionObserver((entries) => {
2627
entries.forEach(entry => {
2728
if (entry.isIntersecting) {
@@ -196,7 +197,7 @@ function createChart(data, containerId, type) {
196197
maxTicksLimit: 10
197198
}
198199
};
199-
200+
200201
// Add dependencies version change annotations
201202
if (Object.keys(data.runs).length > 0) {
202203
ChartAnnotations.addVersionChangeAnnotations(data, options);
@@ -210,8 +211,8 @@ function createChart(data, containerId, type) {
210211
...runData,
211212
// For timeseries (historical results charts) use runName,
212213
// otherwise use displayLabel (for layer comparison charts)
213-
label: containerId.startsWith('timeseries') ?
214-
runData.runName :
214+
label: containerId.startsWith('timeseries') ?
215+
runData.runName :
215216
(runData.displayLabel || runData.label)
216217
}))
217218
} : {
@@ -223,12 +224,12 @@ function createChart(data, containerId, type) {
223224

224225
const chart = new Chart(ctx, chartConfig);
225226
chartInstances.set(containerId, chart);
226-
227+
227228
// Add annotation interaction handlers for time-series charts
228229
if (type === 'time') {
229230
ChartAnnotations.setupAnnotationListeners(chart, ctx, options);
230231
}
231-
232+
232233
return chart;
233234
}
234235

@@ -263,7 +264,7 @@ function drawCharts(filteredTimeseriesData, filteredBarChartsData, filteredLayer
263264
chartInstances.forEach(chart => chart.destroy());
264265
chartInstances.clear();
265266
pendingCharts.clear();
266-
267+
267268
initChartObserver(); // For lazy loading charts
268269

269270
// Create timeseries charts
@@ -394,7 +395,7 @@ function metadataForLabel(label, type) {
394395
if (benchmarkMetadata[label]?.type === type) {
395396
return benchmarkMetadata[label];
396397
}
397-
398+
398399
// Then fall back to prefix match for backward compatibility
399400
for (const [key, metadata] of Object.entries(benchmarkMetadata)) {
400401
if (metadata.type === type && label.startsWith(key)) {
@@ -578,6 +579,12 @@ function updateURL() {
578579
url.searchParams.set('customRange', 'true');
579580
}
580581

582+
if (!isArchivedDataEnabled()) {
583+
url.searchParams.delete('archived');
584+
} else {
585+
url.searchParams.set('archived', 'true');
586+
}
587+
581588
history.replaceState(null, '', url);
582589
}
583590

@@ -835,6 +842,9 @@ function setupRunSelector() {
835842
runSelect = document.getElementById('run-select');
836843
selectedRunsDiv = document.getElementById('selected-runs');
837844

845+
// Clear existing options first to prevent duplicates when reloading with archived data
846+
runSelect.innerHTML = '';
847+
838848
allRunNames.forEach(name => {
839849
const option = document.createElement('option');
840850
option.value = name;
@@ -848,6 +858,9 @@ function setupRunSelector() {
848858
function setupSuiteFilters() {
849859
suiteFiltersContainer = document.getElementById('suite-filters');
850860

861+
// Clear existing suite filters before adding new ones
862+
suiteFiltersContainer.innerHTML = '';
863+
851864
benchmarkRuns.forEach(run => {
852865
run.results.forEach(result => {
853866
suiteNames.add(result.suite);
@@ -883,10 +896,16 @@ function isCustomRangesEnabled() {
883896
return rangesToggle.checked;
884897
}
885898

899+
function isArchivedDataEnabled() {
900+
const archivedDataToggle = document.getElementById('show-archived-data');
901+
return archivedDataToggle.checked;
902+
}
903+
886904
function setupToggles() {
887905
const notesToggle = document.getElementById('show-notes');
888906
const unstableToggle = document.getElementById('show-unstable');
889907
const customRangeToggle = document.getElementById('custom-range');
908+
const archivedDataToggle = document.getElementById('show-archived-data');
890909

891910
notesToggle.addEventListener('change', function () {
892911
// Update all note elements visibility
@@ -909,9 +928,25 @@ function setupToggles() {
909928
updateCharts();
910929
});
911930

931+
// Add event listener for archived data toggle
932+
if (archivedDataToggle) {
933+
archivedDataToggle.addEventListener('change', function() {
934+
if (archivedDataToggle.checked) {
935+
loadArchivedData();
936+
} else {
937+
if (archivedDataLoaded) {
938+
// Reload the page to reset
939+
location.reload();
940+
}
941+
}
942+
updateURL();
943+
});
944+
}
945+
912946
// Initialize from URL params if present
913947
const notesParam = getQueryParam('notes');
914948
const unstableParam = getQueryParam('unstable');
949+
const archivedParam = getQueryParam('archived');
915950

916951
if (notesParam !== null) {
917952
let showNotes = notesParam === 'true';
@@ -927,11 +962,22 @@ function setupToggles() {
927962
if (customRangesParam !== null) {
928963
customRangeToggle.checked = customRangesParam === 'true';
929964
}
965+
966+
if (archivedDataToggle && archivedParam !== null) {
967+
archivedDataToggle.checked = archivedParam === 'true';
968+
969+
if (archivedDataToggle.checked) {
970+
loadArchivedData();
971+
}
972+
}
930973
}
931974

932975
function setupTagFilters() {
933976
tagFiltersContainer = document.getElementById('tag-filters');
934977

978+
// Clear existing tag filters before adding new ones
979+
tagFiltersContainer.innerHTML = '';
980+
935981
const allTags = [];
936982

937983
if (benchmarkTags) {
@@ -1087,38 +1133,124 @@ window.addSelectedRun = addSelectedRun;
10871133
window.removeRun = removeRun;
10881134
window.toggleAllTags = toggleAllTags;
10891135

1136+
// Helper function to fetch and process benchmark data
1137+
function fetchAndProcessData(url, isArchived = false) {
1138+
const loadingIndicator = document.getElementById('loading-indicator');
1139+
1140+
return fetch(url)
1141+
.then(response => {
1142+
if (!response.ok) { throw new Error(`Got response status ${response.status}.`) }
1143+
return response.json();
1144+
})
1145+
.then(data => {
1146+
const newRuns = data.runs || data;
1147+
1148+
if (isArchived) {
1149+
// Merge with existing data for archived data
1150+
benchmarkRuns = benchmarkRuns.concat(newRuns);
1151+
1152+
// Merge metadata and tags if available
1153+
if (data.metadata) {
1154+
benchmarkMetadata = { ...benchmarkMetadata, ...data.metadata };
1155+
}
1156+
1157+
if (data.tags) {
1158+
benchmarkTags = { ...benchmarkTags, ...data.tags };
1159+
}
1160+
1161+
archivedDataLoaded = true;
1162+
} else {
1163+
// Replace existing data for primary data
1164+
benchmarkRuns = newRuns;
1165+
benchmarkMetadata = data.metadata || benchmarkMetadata || {};
1166+
benchmarkTags = data.tags || benchmarkTags || {};
1167+
}
1168+
1169+
initializeCharts();
1170+
})
1171+
.catch(error => {
1172+
console.error(`Error fetching ${isArchived ? 'archived' : 'remote'} data:`, error);
1173+
loadingIndicator.textContent = 'Fetching remote data failed.';
1174+
})
1175+
.finally(() => {
1176+
loadingIndicator.style.display = 'none';
1177+
});
1178+
}
1179+
10901180
// Load data based on configuration
10911181
function loadData() {
10921182
const loadingIndicator = document.getElementById('loading-indicator');
10931183
loadingIndicator.style.display = 'block'; // Show loading indicator
10941184

10951185
if (typeof remoteDataUrl !== 'undefined' && remoteDataUrl !== '') {
10961186
// Fetch data from remote URL
1097-
fetch(remoteDataUrl)
1098-
.then(response => {
1099-
if (!response.ok) { throw new Error(`Got response status ${response.status}.`) }
1100-
return response.json();
1101-
})
1102-
.then(data => {
1103-
benchmarkRuns = data.runs || data;
1104-
benchmarkMetadata = data.metadata || benchmarkMetadata || {};
1105-
benchmarkTags = data.tags || benchmarkTags || {};
1106-
initializeCharts();
1107-
})
1108-
.catch(error => {
1109-
console.error('Error fetching remote data:', error);
1110-
loadingIndicator.textContent = 'Fetching remote data failed.';
1111-
})
1112-
.finally(() => {
1113-
loadingIndicator.style.display = 'none'; // Hide loading indicator
1114-
});
1187+
fetchAndProcessData(remoteDataUrl);
11151188
} else {
11161189
// Use local data (benchmarkRuns and benchmarkMetadata should be defined in data.js)
11171190
initializeCharts();
11181191
loadingIndicator.style.display = 'none'; // Hide loading indicator
11191192
}
11201193
}
11211194

1195+
// Function to load archived data and merge with current data
1196+
// Archived data consists of older benchmark results that have been separated from
1197+
// the primary dataset but are still available for historical analysis.
1198+
function loadArchivedData() {
1199+
const loadingIndicator = document.getElementById('loading-indicator');
1200+
loadingIndicator.style.display = 'block';
1201+
1202+
if (archivedDataLoaded) {
1203+
updateCharts();
1204+
loadingIndicator.style.display = 'none';
1205+
return;
1206+
}
1207+
1208+
if (typeof remoteDataUrl !== 'undefined' && remoteDataUrl !== '') {
1209+
// For remote data, construct the archive URL by adding _archive before the extension
1210+
const archiveUrl = remoteDataUrl.replace(/(\.[^.]+)$/, '_archive$1');
1211+
1212+
// Check if we're using local JSON files
1213+
if (remoteDataUrl.startsWith('./') && remoteDataUrl.endsWith('.json')) {
1214+
fetchAndProcessData(archiveUrl, true);
1215+
} else {
1216+
fetchAndProcessData(archiveUrl, true);
1217+
}
1218+
} else {
1219+
// For local data using a static js file
1220+
const script = document.createElement('script');
1221+
script.src = 'data_archive.js';
1222+
script.onload = () => {
1223+
if (typeof archivedBenchmarkRuns !== 'undefined') {
1224+
// Merge the archived runs with current runs
1225+
benchmarkRuns = benchmarkRuns.concat(archivedBenchmarkRuns);
1226+
1227+
// Merge metadata and tags if available
1228+
if (typeof archivedBenchmarkMetadata !== 'undefined') {
1229+
benchmarkMetadata = { ...benchmarkMetadata, ...archivedBenchmarkMetadata };
1230+
}
1231+
1232+
if (typeof archivedBenchmarkTags !== 'undefined') {
1233+
benchmarkTags = { ...benchmarkTags, ...archivedBenchmarkTags };
1234+
}
1235+
1236+
archivedDataLoaded = true;
1237+
1238+
initializeCharts();
1239+
} else {
1240+
console.error('Archived runs data not found in data_archive.js');
1241+
}
1242+
loadingIndicator.style.display = 'none';
1243+
};
1244+
1245+
script.onerror = () => {
1246+
console.error('Failed to load data_archive.js');
1247+
loadingIndicator.style.display = 'none';
1248+
};
1249+
1250+
document.head.appendChild(script);
1251+
}
1252+
}
1253+
11221254
// Initialize when DOM is ready
11231255
document.addEventListener('DOMContentLoaded', () => {
11241256
loadData();

devops/scripts/benchmarks/main.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ def main(directory, additional_env_vars, save_name, compare_names, filter):
316316
if options.output_directory is None:
317317
html_path = os.path.join(os.path.dirname(__file__), "html")
318318

319-
generate_html(history.runs, compare_names, html_path, metadata)
319+
generate_html(history, compare_names, html_path, metadata)
320320

321321

322322
def validate_and_parse_env_args(env_args):
@@ -558,6 +558,22 @@ def validate_and_parse_env_args(env_args):
558558
help="Location of detect_version.cpp used to query e.g. DPC++, L0",
559559
default=None,
560560
)
561+
parser.add_argument(
562+
"--archive-baseline-after",
563+
type=int,
564+
help="Archive baseline results (runs starting with 'Baseline_') older than this many days. "
565+
"Archived results are stored separately and can be viewed in the HTML UI by enabling "
566+
"'Include archived runs'. This helps manage the size of the primary dataset.",
567+
default=options.archive_baseline_days,
568+
)
569+
parser.add_argument(
570+
"--archive-pr-after",
571+
type=int,
572+
help="Archive PR and other non-baseline results older than this many days. "
573+
"Archived results are stored separately and can be viewed in the HTML UI by enabling "
574+
"'Include archived runs'. PR runs typically have a shorter retention period than baselines.",
575+
default=options.archive_pr_days,
576+
)
561577

562578
args = parser.parse_args()
563579
additional_env_vars = validate_and_parse_env_args(args.env)

0 commit comments

Comments
 (0)