mirror of
https://github.com/dolthub/dolt.git
synced 2026-01-24 03:09:22 -06:00
Improvements to the perf viewer (#2499)
* Increase number of results to 20. * See newer results at the end of the graph, not start. * Make point radius show standard deviation to scale. * Try not to draw datasets over each other. * Invert light colours to make them darker.
This commit is contained in:
@@ -28,10 +28,11 @@ window.onpopstate = load;
|
||||
window.onresize = render;
|
||||
|
||||
// The maximum number of git revisions to show in the perf history.
|
||||
//
|
||||
// The larger this number, the more screen real estate needed to render the graph - and the slower
|
||||
// it will take to render, since the entire parent commit chain must be walked to form the graph.
|
||||
// TODO: Implement paging mechanism.
|
||||
const MAX_PERF_HISTORY = 15;
|
||||
const MAX_PERF_HISTORY = 20;
|
||||
|
||||
let chartDatasets: Map<string /* test name */, (DataPoint | null)[]>;
|
||||
let chartLabels: string[];
|
||||
@@ -97,8 +98,8 @@ async function getPerfHistory(ds: Dataset): Promise<[Struct[], string[]]> {
|
||||
for (let head = await ds.head(), i = 0; head && i < MAX_PERF_HISTORY; i++) {
|
||||
const val = head.value;
|
||||
invariant(val instanceof Struct);
|
||||
perfData.push(val);
|
||||
gitRevs.push(val.nomsRevision);
|
||||
perfData.unshift(val);
|
||||
gitRevs.unshift(val.nomsRevision);
|
||||
|
||||
const parentRef = await head.parents.first(); // TODO: how to deal with multiple parents?
|
||||
head = parentRef ? await parentRef.targetValue(ds.database) : null;
|
||||
@@ -126,19 +127,38 @@ async function render() {
|
||||
return;
|
||||
}
|
||||
|
||||
// We use the point radius to indicate the standard deviation, for lack of any better option.
|
||||
// Unfortunately chart.js doesn't provide any way to scale this relative to large the Y axis
|
||||
// values are with respect to the graph pixel height.
|
||||
//
|
||||
// So, try to approximate it by taking into account: (a) the expected magnitude of the Y axis (the
|
||||
// maximum value), and (b) and how much space the graph will take up on the screen (half of screen
|
||||
// *width* - this does appear to be what chart.js does).
|
||||
const maxElapsedTime = Array.from(chartDatasets.values()).reduce((max, dataPoints) => {
|
||||
const medians = dataPoints.map(dp => dp !== null ? dp.median : 0);
|
||||
return Math.max(max, ...medians);
|
||||
}, 0);
|
||||
const graphHeight = document.body.scrollWidth / 2;
|
||||
const getStddevPointRadius = stddev => Math.ceil(stddev / maxElapsedTime * graphHeight);
|
||||
|
||||
const datasets = [];
|
||||
for (const [testName, dataPoints] of chartDatasets) {
|
||||
const [borderColor, backgroundColor] = await getSolidAndAlphaColors(testName);
|
||||
const [borderColor, backgroundColor] = getSolidAndAlphaColors(testName);
|
||||
datasets.push({
|
||||
backgroundColor,
|
||||
borderColor,
|
||||
borderWidth: 1,
|
||||
pointRadius: dataPoints.map(dp => dp !== null ? 1 + dp.stddev : 0),
|
||||
pointRadius: dataPoints.map(dp => dp !== null ? getStddevPointRadius(dp.stddev) : 0),
|
||||
data: dataPoints.map(dp => dp !== null ? dp.median : null),
|
||||
label: testName,
|
||||
_maxMedian: Math.max(...dataPoints.map(dp => dp !== null ? dp.median : 0)), // for our sorting
|
||||
});
|
||||
}
|
||||
|
||||
// Draw the datasets in order of largest to smallest, so that we (try not to) draw over the top of
|
||||
// entire datasets.
|
||||
datasets.sort((a, b) => a._maxMedian - b._maxMedian);
|
||||
|
||||
new Chart(document.getElementById('chart'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
@@ -150,7 +170,7 @@ async function render() {
|
||||
yAxes: [{
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'elapsed (seconds)',
|
||||
labelString: 'elapsed seconds (point radius is standard deviation to scale)',
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
@@ -178,19 +198,22 @@ function makeDataPoint(nums: number[]): DataPoint {
|
||||
median /= 2;
|
||||
}
|
||||
|
||||
const calcMean = ns => ns.reduce((t, n) => t + n, 0) / ns.length;
|
||||
const mean = calcMean(nums);
|
||||
const stddev = Math.sqrt(calcMean(nums.map(n => Math.pow(n - mean, 2))));
|
||||
const mean = getMean(nums);
|
||||
const stddev = Math.sqrt(getMean(nums.map(n => Math.pow(n - mean, 2))));
|
||||
|
||||
return {median, stddev};
|
||||
}
|
||||
|
||||
// Generates a light and dark version of some color randomly (but stable) derived from `str`.
|
||||
async function getSolidAndAlphaColors(str: string): Promise<[string, string]> {
|
||||
function getSolidAndAlphaColors(str: string): [string, string] {
|
||||
// getHashOfValue() returns a Uint8Array, so pull out the first 3 8-bit numbers - which will be in
|
||||
// the range [0, 255] - to generate a full RGB colour.
|
||||
const [r, g, b] = getHashOfValue(str).digest;
|
||||
return [`rgb(${r}, ${g}, ${b})`, `rgba(${r}, ${g}, ${b}, 0.25)`];
|
||||
let [r, g, b] = getHashOfValue(str).digest;
|
||||
// Invert if it's too light.
|
||||
if (getMean([r, g, b]) > 127) {
|
||||
[r, g, b] = [r, g, b].map(c => 255 - c);
|
||||
}
|
||||
return [`rgb(${r}, ${g}, ${b})`, `rgba(${r}, ${g}, ${b}, 0.2)`];
|
||||
}
|
||||
|
||||
// Returns the keys of `map`.
|
||||
@@ -200,3 +223,8 @@ function keys<K: Value, V: Value>(map: NomsMap<K, V>): Promise<K[]> {
|
||||
keys.push(key);
|
||||
}).then(() => keys);
|
||||
}
|
||||
|
||||
// Returns the mean of `nums`.
|
||||
function getMean(nums: number[]): number {
|
||||
return nums.reduce((t, n) => t + n, 0) / nums.length;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user