mirror of
https://github.com/django-components/django-components.git
synced 2025-08-30 02:44:06 +00:00
feat: benchmarking (#999)
* feat: add benchmarking dashboard, CI hook on PR, and store lifetime results * refactor: change python env to 3.13 in benchmarks * refactor: add verbosity, use 3.11 for benchmarking * fix: OSError: [Errno 7] Argument list too long * refactor: add debug statements * refactor: remove extraneous -e * refactor: fix tests and linter errors * fix: track main package in coverage * refactor: fix test coverage testing * refactor: fix repo owner name in benchmark on pushing comment * refactor: add asv monkeypatch to docs workflow * refactor: temporarily allow building docs in forks * refactor: use py 3.13 for benchmarking * refactor: run only a single benchmark for PRs to speed them up * refactor: install asv in the docs build workflow * refactor: use hatch docs env to generate benhcmarks in docs CI * refactor: more trying * refactor: move tests * Add benchmark results for 0.137 * Trigger Build * Add benchmark results for 0.138 * refactor: set constant machine name when benchmarking * Add benchmark results for 0.139 * refactor: fix issue with paths too long * Add benchmark results for 0.140 * docs: update comment * refactor: remove test benchmarking data * refactor: fix comment * refactor: allow the benchmark workflow to write to PRs * refactor: use personal access token to set up the PR benchmark bot * refactor: split the benchmark PR flow into two to make it work with PRs from forks * refactor: update deprecated actions/upload-artifact@v3 to v4 * refactor: fix missing directory in benchmarking workflow * refactor: fix triggering of second workflow * refactor: fix workflow finally? * docs: add comments to cut-offs and direct people to benchmarks PR --------- Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
parent
dcd4203eea
commit
f36581ed86
90 changed files with 40817 additions and 443 deletions
525
docs/benchmarks/asv.js
Normal file
525
docs/benchmarks/asv.js
Normal file
|
@ -0,0 +1,525 @@
|
|||
'use strict';
|
||||
|
||||
$(document).ready(function() {
|
||||
/* GLOBAL STATE */
|
||||
/* The index.json content as returned from the server */
|
||||
var main_timestamp = '';
|
||||
var main_json = {};
|
||||
/* Extra pages: {name: show_function} */
|
||||
var loaded_pages = {};
|
||||
/* Previous window scroll positions */
|
||||
var window_scroll_positions = {};
|
||||
/* Previous window hash location */
|
||||
var window_last_location = null;
|
||||
/* Graph data cache */
|
||||
var graph_cache = {};
|
||||
var graph_cache_max_size = 5;
|
||||
|
||||
var colors = [
|
||||
'#247AAD',
|
||||
'#E24A33',
|
||||
'#988ED5',
|
||||
'#777777',
|
||||
'#FBC15E',
|
||||
'#8EBA42',
|
||||
'#FFB5B8'
|
||||
];
|
||||
|
||||
var time_units = [
|
||||
['ps', 'picoseconds', 0.000000000001],
|
||||
['ns', 'nanoseconds', 0.000000001],
|
||||
['μs', 'microseconds', 0.000001],
|
||||
['ms', 'milliseconds', 0.001],
|
||||
['s', 'seconds', 1],
|
||||
['m', 'minutes', 60],
|
||||
['h', 'hours', 60 * 60],
|
||||
['d', 'days', 60 * 60 * 24],
|
||||
['w', 'weeks', 60 * 60 * 24 * 7],
|
||||
['y', 'years', 60 * 60 * 24 * 7 * 52],
|
||||
['C', 'centuries', 60 * 60 * 24 * 7 * 52 * 100]
|
||||
];
|
||||
|
||||
var mem_units = [
|
||||
['', 'bytes', 1],
|
||||
['k', 'kilobytes', 1000],
|
||||
['M', 'megabytes', 1000000],
|
||||
['G', 'gigabytes', 1000000000],
|
||||
['T', 'terabytes', 1000000000000]
|
||||
];
|
||||
|
||||
function pretty_second(x) {
|
||||
for (var i = 0; i < time_units.length - 1; ++i) {
|
||||
if (Math.abs(x) < time_units[i+1][2]) {
|
||||
return (x / time_units[i][2]).toFixed(3) + time_units[i][0];
|
||||
}
|
||||
}
|
||||
|
||||
return 'inf';
|
||||
}
|
||||
|
||||
function pretty_byte(x) {
|
||||
for (var i = 0; i < mem_units.length - 1; ++i) {
|
||||
if (Math.abs(x) < mem_units[i+1][2]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 0) {
|
||||
return x + '';
|
||||
}
|
||||
return (x / mem_units[i][2]).toFixed(3) + mem_units[i][0];
|
||||
}
|
||||
|
||||
function pretty_unit(x, unit) {
|
||||
if (unit == "seconds") {
|
||||
return pretty_second(x);
|
||||
}
|
||||
else if (unit == "bytes") {
|
||||
return pretty_byte(x);
|
||||
}
|
||||
else if (unit && unit != "unit") {
|
||||
return '' + x.toPrecision(3) + ' ' + unit;
|
||||
}
|
||||
else {
|
||||
return '' + x.toPrecision(3);
|
||||
}
|
||||
}
|
||||
|
||||
function pad_left(s, c, num) {
|
||||
s = '' + s;
|
||||
while (s.length < num) {
|
||||
s = c + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function format_date_yyyymmdd(date) {
|
||||
return (pad_left(date.getFullYear(), '0', 4)
|
||||
+ '-' + pad_left(date.getMonth() + 1, '0', 2)
|
||||
+ '-' + pad_left(date.getDate(), '0', 2));
|
||||
}
|
||||
|
||||
function format_date_yyyymmdd_hhmm(date) {
|
||||
return (format_date_yyyymmdd(date) + ' '
|
||||
+ pad_left(date.getHours(), '0', 2)
|
||||
+ ':' + pad_left(date.getMinutes(), '0', 2));
|
||||
}
|
||||
|
||||
/* Convert a flat index to permutation to the corresponding value */
|
||||
function param_selection_from_flat_idx(params, idx) {
|
||||
var selection = [];
|
||||
if (idx < 0) {
|
||||
idx = 0;
|
||||
}
|
||||
for (var k = params.length-1; k >= 0; --k) {
|
||||
var j = idx % params[k].length;
|
||||
selection.unshift([j]);
|
||||
idx = (idx - j) / params[k].length;
|
||||
}
|
||||
selection.unshift([null]);
|
||||
return selection;
|
||||
}
|
||||
|
||||
/* Convert a benchmark parameter value from their native Python
|
||||
repr format to a number or a string, ready for presentation */
|
||||
function convert_benchmark_param_value(value_repr) {
|
||||
var match = Number(value_repr);
|
||||
if (!isNaN(match)) {
|
||||
return match;
|
||||
}
|
||||
|
||||
/* Python str */
|
||||
match = value_repr.match(/^'(.+)'$/);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
/* Python unicode */
|
||||
match = value_repr.match(/^u'(.+)'$/);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
/* Python class */
|
||||
match = value_repr.match(/^<class '(.+)'>$/);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
return value_repr;
|
||||
}
|
||||
|
||||
/* Convert loaded graph data to a format flot understands, by
|
||||
treating either time or one of the parameters as x-axis,
|
||||
and selecting only one value of the remaining axes */
|
||||
function filter_graph_data(raw_series, x_axis, other_indices, params) {
|
||||
if (params.length == 0) {
|
||||
/* Simple time series */
|
||||
return raw_series;
|
||||
}
|
||||
|
||||
/* Compute position of data entry in the results list,
|
||||
and stride corresponding to plot x-axis parameter */
|
||||
var stride = 1;
|
||||
var param_stride = 0;
|
||||
var param_idx = 0;
|
||||
for (var k = params.length - 1; k >= 0; --k) {
|
||||
if (k == x_axis - 1) {
|
||||
param_stride = stride;
|
||||
}
|
||||
else {
|
||||
param_idx += other_indices[k + 1] * stride;
|
||||
}
|
||||
stride *= params[k].length;
|
||||
}
|
||||
|
||||
if (x_axis == 0) {
|
||||
/* x-axis is time axis */
|
||||
var series = new Array(raw_series.length);
|
||||
for (var k = 0; k < raw_series.length; ++k) {
|
||||
if (raw_series[k][1] === null) {
|
||||
series[k] = [raw_series[k][0], null];
|
||||
} else {
|
||||
series[k] = [raw_series[k][0],
|
||||
raw_series[k][1][param_idx]];
|
||||
}
|
||||
}
|
||||
return series;
|
||||
}
|
||||
else {
|
||||
/* x-axis is some parameter axis */
|
||||
var time_idx = null;
|
||||
if (other_indices[0] === null) {
|
||||
time_idx = raw_series.length - 1;
|
||||
}
|
||||
else {
|
||||
/* Need to search for the correct time value */
|
||||
for (var k = 0; k < raw_series.length; ++k) {
|
||||
if (raw_series[k][0] == other_indices[0]) {
|
||||
time_idx = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (time_idx === null) {
|
||||
/* No data points */
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
var x_values = params[x_axis - 1];
|
||||
var series = new Array(x_values.length);
|
||||
for (var k = 0; k < x_values.length; ++k) {
|
||||
if (raw_series[time_idx][1] === null) {
|
||||
series[k] = [convert_benchmark_param_value(x_values[k]),
|
||||
null];
|
||||
}
|
||||
else {
|
||||
series[k] = [convert_benchmark_param_value(x_values[k]),
|
||||
raw_series[time_idx][1][param_idx]];
|
||||
}
|
||||
param_idx += param_stride;
|
||||
}
|
||||
return series;
|
||||
}
|
||||
}
|
||||
|
||||
function filter_graph_data_idx(raw_series, x_axis, flat_idx, params) {
|
||||
var selection = param_selection_from_flat_idx(params, flat_idx);
|
||||
var flat_selection = [];
|
||||
$.each(selection, function(i, v) {
|
||||
flat_selection.push(v[0]);
|
||||
});
|
||||
return filter_graph_data(raw_series, x_axis, flat_selection, params);
|
||||
}
|
||||
|
||||
/* Escape special characters in graph item file names.
|
||||
The implementation must match asv.util.sanitize_filename */
|
||||
function sanitize_filename(name) {
|
||||
var bad_re = /[<>:"\/\\^|?*\x00-\x1f]/g;
|
||||
var bad_names = ["CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3",
|
||||
"COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1",
|
||||
"LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8",
|
||||
"LPT9"];
|
||||
name = name.replace(bad_re, "_");
|
||||
if (bad_names.indexOf(name.toUpperCase()) != -1) {
|
||||
name = name + "_";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/* Given a specific group of parameters, generate the URL to
|
||||
use to load that graph.
|
||||
The implementation must match asv.graph.Graph.get_file_path
|
||||
*/
|
||||
function graph_to_path(benchmark_name, state) {
|
||||
var parts = [];
|
||||
$.each(state, function(key, value) {
|
||||
var part;
|
||||
if (value === null) {
|
||||
part = key + "-null";
|
||||
} else if (value) {
|
||||
part = key + "-" + value;
|
||||
} else {
|
||||
part = key;
|
||||
}
|
||||
parts.push(sanitize_filename('' + part));
|
||||
});
|
||||
parts.sort();
|
||||
parts.splice(0, 0, "graphs");
|
||||
parts.push(sanitize_filename(benchmark_name));
|
||||
|
||||
/* Escape URI components */
|
||||
parts = $.map(parts, function (val) { return encodeURIComponent(val); });
|
||||
return parts.join('/') + ".json";
|
||||
}
|
||||
|
||||
/*
|
||||
Load and cache graph data (on javascript side)
|
||||
*/
|
||||
function load_graph_data(url, success, failure) {
|
||||
var dfd = $.Deferred();
|
||||
if (graph_cache[url]) {
|
||||
setTimeout(function() {
|
||||
dfd.resolve(graph_cache[url]);
|
||||
}, 1);
|
||||
}
|
||||
else {
|
||||
$.ajax({
|
||||
url: url + '?timestamp=' + $.asv.main_timestamp,
|
||||
dataType: "json",
|
||||
cache: true
|
||||
}).done(function(data) {
|
||||
if (Object.keys(graph_cache).length > graph_cache_max_size) {
|
||||
$.each(Object.keys(graph_cache), function (i, key) {
|
||||
delete graph_cache[key];
|
||||
});
|
||||
}
|
||||
graph_cache[url] = data;
|
||||
dfd.resolve(data);
|
||||
}).fail(function() {
|
||||
dfd.reject();
|
||||
});
|
||||
}
|
||||
return dfd.promise();
|
||||
}
|
||||
|
||||
/*
|
||||
Parse hash string, assuming format similar to standard URL
|
||||
query strings
|
||||
*/
|
||||
function parse_hash_string(str) {
|
||||
var info = {location: [''], params: {}};
|
||||
|
||||
if (str && str[0] == '#') {
|
||||
str = str.slice(1);
|
||||
}
|
||||
if (str && str[0] == '/') {
|
||||
str = str.slice(1);
|
||||
}
|
||||
|
||||
var match = str.match(/^([^?]*?)\?/);
|
||||
if (match) {
|
||||
info['location'] = decodeURIComponent(match[1]).replace(/\/+/, '/').split('/');
|
||||
var rest = str.slice(match[1].length+1);
|
||||
var parts = rest.split('&');
|
||||
for (var i = 0; i < parts.length; ++i) {
|
||||
var part = parts[i].split('=');
|
||||
if (part.length != 2) {
|
||||
continue;
|
||||
}
|
||||
var key = decodeURIComponent(part[0].replace(/\+/g, " "));
|
||||
var value = decodeURIComponent(part[1].replace(/\+/g, " "));
|
||||
if (value == '[none]') {
|
||||
value = null;
|
||||
}
|
||||
if (info['params'][key] === undefined) {
|
||||
info['params'][key] = [value];
|
||||
}
|
||||
else {
|
||||
info['params'][key].push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
info['location'] = decodeURIComponent(str).replace(/\/+/, '/').split('/');
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a hash string, inverse of parse_hash_string
|
||||
*/
|
||||
function format_hash_string(info) {
|
||||
var parts = info['params'];
|
||||
var str = '#' + info['location'];
|
||||
|
||||
if (parts) {
|
||||
str = str + '?';
|
||||
var first = true;
|
||||
$.each(parts, function (key, values) {
|
||||
$.each(values, function (idx, value) {
|
||||
if (!first) {
|
||||
str = str + '&';
|
||||
}
|
||||
if (value === null) {
|
||||
value = '[none]';
|
||||
}
|
||||
str = str + encodeURIComponent(key) + '=' + encodeURIComponent(value);
|
||||
first = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
Dealing with sub-pages
|
||||
*/
|
||||
|
||||
function show_page(name, params) {
|
||||
if (loaded_pages[name] !== undefined) {
|
||||
$("#nav ul li.active").removeClass('active');
|
||||
$("#nav-li-" + name).addClass('active');
|
||||
$("#graph-display").hide();
|
||||
$("#summarygrid-display").hide();
|
||||
$("#summarylist-display").hide();
|
||||
$('#regressions-display').hide();
|
||||
$('.tooltip').remove();
|
||||
loaded_pages[name](params);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function hashchange() {
|
||||
var info = parse_hash_string(window.location.hash);
|
||||
|
||||
/* Keep track of window scroll position; makes the back-button work */
|
||||
var old_scroll_pos = window_scroll_positions[info.location.join('/')];
|
||||
window_scroll_positions[window_last_location] = $(window).scrollTop();
|
||||
window_last_location = info.location.join('/');
|
||||
|
||||
/* Redirect to correct handler */
|
||||
if (show_page(info.location, info.params)) {
|
||||
/* show_page does the work */
|
||||
}
|
||||
else {
|
||||
/* Display benchmark page */
|
||||
info.params['benchmark'] = info.location[0];
|
||||
show_page('graphdisplay', info.params);
|
||||
}
|
||||
|
||||
/* Scroll back to previous position, if any */
|
||||
if (old_scroll_pos !== undefined) {
|
||||
$(window).scrollTop(old_scroll_pos);
|
||||
}
|
||||
}
|
||||
|
||||
function get_commit_hash(revision) {
|
||||
var commit_hash = main_json.revision_to_hash[revision];
|
||||
if (commit_hash) {
|
||||
// Return printable commit hash
|
||||
commit_hash = commit_hash.slice(0, main_json.hash_length);
|
||||
}
|
||||
return commit_hash;
|
||||
}
|
||||
|
||||
function get_revision(commit_hash) {
|
||||
var rev = null;
|
||||
$.each(main_json.revision_to_hash, function(revision, full_commit_hash) {
|
||||
if (full_commit_hash.startsWith(commit_hash)) {
|
||||
rev = revision;
|
||||
// break the $.each loop
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return rev;
|
||||
}
|
||||
|
||||
function init_index() {
|
||||
/* Fetch the main index.json and then set up the page elements
|
||||
based on it. */
|
||||
$.ajax({
|
||||
url: "index.json" + '?timestamp=' + $.asv.main_timestamp,
|
||||
dataType: "json",
|
||||
cache: true
|
||||
}).done(function (index) {
|
||||
main_json = index;
|
||||
$.asv.main_json = index;
|
||||
|
||||
/* Page title */
|
||||
var project_name = $("#project-name")[0];
|
||||
project_name.textContent = index.project;
|
||||
project_name.setAttribute("href", index.project_url);
|
||||
$("#project-name").textContent = index.project;
|
||||
document.title = "airspeed velocity of an unladen " + index.project;
|
||||
|
||||
$(window).on('hashchange', hashchange);
|
||||
|
||||
$('#graph-display').hide();
|
||||
$('#regressions-display').hide();
|
||||
$('#summarygrid-display').hide();
|
||||
$('#summarylist-display').hide();
|
||||
|
||||
hashchange();
|
||||
}).fail(function () {
|
||||
$.asv.ui.network_error();
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
/* Fetch the info.json */
|
||||
$.ajax({
|
||||
url: "info.json",
|
||||
dataType: "json",
|
||||
cache: false
|
||||
}).done(function (info) {
|
||||
main_timestamp = info['timestamp'];
|
||||
$.asv.main_timestamp = main_timestamp;
|
||||
init_index();
|
||||
}).fail(function () {
|
||||
$.asv.ui.network_error();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Set up $.asv
|
||||
*/
|
||||
|
||||
this.register_page = function(name, show_function) {
|
||||
loaded_pages[name] = show_function;
|
||||
}
|
||||
this.parse_hash_string = parse_hash_string;
|
||||
this.format_hash_string = format_hash_string;
|
||||
|
||||
this.filter_graph_data = filter_graph_data;
|
||||
this.filter_graph_data_idx = filter_graph_data_idx;
|
||||
this.convert_benchmark_param_value = convert_benchmark_param_value;
|
||||
this.param_selection_from_flat_idx = param_selection_from_flat_idx;
|
||||
this.graph_to_path = graph_to_path;
|
||||
this.load_graph_data = load_graph_data;
|
||||
this.get_commit_hash = get_commit_hash;
|
||||
this.get_revision = get_revision;
|
||||
|
||||
this.main_timestamp = main_timestamp; /* Updated after info.json loads */
|
||||
this.main_json = main_json; /* Updated after index.json loads */
|
||||
|
||||
this.format_date_yyyymmdd = format_date_yyyymmdd;
|
||||
this.format_date_yyyymmdd_hhmm = format_date_yyyymmdd_hhmm;
|
||||
this.pretty_unit = pretty_unit;
|
||||
this.time_units = time_units;
|
||||
this.mem_units = mem_units;
|
||||
|
||||
this.colors = colors;
|
||||
|
||||
$.asv = this;
|
||||
|
||||
|
||||
/*
|
||||
Launch it
|
||||
*/
|
||||
|
||||
init();
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue