Skip to content

Commit

Permalink
feat(rum-explorer): perf tweaks (backport from oversight)
Browse files Browse the repository at this point in the history
  • Loading branch information
karlpauls committed Sep 17, 2024
1 parent 5685b6f commit 63abe86
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 70 deletions.
68 changes: 36 additions & 32 deletions tools/rum/charts/skyline.js
Original file line number Diff line number Diff line change
Expand Up @@ -514,39 +514,43 @@ export default class SkylineChart extends AbstractChart {
const group = this.dataChunks.group(this.groupBy);
const chartLabels = Object.keys(group).sort();

const iGoodLCPs = Object.entries(this.dataChunks.aggregates)
const {
iGoodLCPs,
iNiLCPs,
iPoorLCPs,
iGoodCLSs,
iNiCLSs,
iPoorCLSs,
iGoodINPs,
iNiINPs,
iPoorINPs,
allTraffic,
} = Object.entries(this.dataChunks.aggregates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([, totals]) => -totals.iGoodLCP.weight);
const iNiLCPs = Object.entries(this.dataChunks.aggregates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([, totals]) => -totals.iNiLCP.weight);
const iPoorLCPs = Object.entries(this.dataChunks.aggregates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([, totals]) => -totals.iPoorLCP.weight);

const iGoodCLSs = Object.entries(this.dataChunks.aggregates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([, totals]) => -totals.iGoodCLS.weight);
const iNiCLSs = Object.entries(this.dataChunks.aggregates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([, totals]) => -totals.iNiCLS.weight);
const iPoorCLSs = Object.entries(this.dataChunks.aggregates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([, totals]) => -totals.iPoorCLS.weight);

const iGoodINPs = Object.entries(this.dataChunks.aggregates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([, totals]) => -totals.iGoodINP.weight);
const iNiINPs = Object.entries(this.dataChunks.aggregates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([, totals]) => -totals.iNiINP.weight);
const iPoorINPs = Object.entries(this.dataChunks.aggregates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([, totals]) => -totals.iPoorINP.weight);

const allTraffic = Object.entries(this.dataChunks.aggregates)
.sort(([a], [b]) => a.localeCompare(b))
.map(([, totals]) => totals.pageViews.sum);
.reduce((acc, [, totals]) => {
acc.iGoodLCPs.push(-totals.iGoodLCP.weight);
acc.iNiLCPs.push(-totals.iNiLCP.weight);
acc.iPoorLCPs.push(-totals.iPoorLCP.weight);
acc.iGoodCLSs.push(-totals.iGoodCLS.weight);
acc.iNiCLSs.push(-totals.iNiCLS.weight);
acc.iPoorCLSs.push(-totals.iPoorCLS.weight);
acc.iGoodINPs.push(-totals.iGoodINP.weight);
acc.iNiINPs.push(-totals.iNiINP.weight);
acc.iPoorINPs.push(-totals.iPoorINP.weight);
acc.allTraffic.push(totals.pageViews.sum);
return acc;
}, {
iGoodLCPs: [],
iNiLCPs: [],
iPoorLCPs: [],
iGoodCLSs: [],
iNiCLSs: [],
iPoorCLSs: [],
iGoodINPs: [],
iNiINPs: [],
iPoorINPs: [],
allTraffic: [],
});

this.chart.data.datasets[0].data = allTraffic;

Expand Down
40 changes: 19 additions & 21 deletions tools/rum/cruncher.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,16 @@ function groupFn(groupByFn) {
* @typedef {Object} Aggregate - an object that contains aggregate metrics
*/
class Aggregate {
constructor(parent = null) {
constructor(parentProvider = () => null) {
this.count = 0;
this.sum = 0;
this.weight = 0;
this.values = [];
this.parent = parent;
this.parentProvider = parentProvider;
}

get parent() {
return this.parentProvider();
}

get min() {
Expand Down Expand Up @@ -654,14 +658,11 @@ export class DataChunks {
.filter(skipFilterFn)
.filter(([, desiredValues]) => desiredValues.length)
.filter(existenceFilterFn);
return bundles.filter((bundle) => {
const matches = filterBy.map(([attributeName, desiredValues]) => {
const actualValues = valuesExtractorFn(attributeName, bundle, this);
const combiner = combinerExtractorFn(attributeName, this);
return desiredValues[combiner]((value) => actualValues.includes(value));
});
return matches.every((match) => match);
});
return bundles.filter((bundle) => filterBy.every(([attributeName, desiredValues]) => {
const actualValues = valuesExtractorFn(attributeName, bundle, this);
const combiner = combinerExtractorFn(attributeName, this);
return desiredValues[combiner]((value) => actualValues.includes(value));
}));
}

/**
Expand Down Expand Up @@ -764,7 +765,7 @@ export class DataChunks {
aggregateFn(valueFn),
// we reference the totals object here, so that we can
// calculate the share and percentage metrics
new Aggregate(this.totals[seriesName]),
new Aggregate(() => this.totals[seriesName]),
);
return accInner;
}, {});
Expand Down Expand Up @@ -810,20 +811,17 @@ export class DataChunks {
// go over each function in this.series and each value in filteredIn
// and appy the function to the value
if (Object.keys(this.totalsIn).length) return this.totalsIn;
const parentTotals = Object.entries(this.series)
.reduce((acc, [seriesName, valueFn]) => {
acc[seriesName] = this.filtered.reduce(
aggregateFn(valueFn),
new Aggregate(),
);
return acc;
}, {});
this.totalsIn = Object.entries(this.series)
.reduce((acc, [seriesName, valueFn]) => {
acc[seriesName] = this.filtered.reduce(
const parent = this.filtered.reduce(
aggregateFn(valueFn),
new Aggregate(parentTotals[seriesName]),
new Aggregate(),
);
// we need to clone the aggregate object, so that we can use it as its own parent
// this is necessary for calculating the share and percentage metrics
// the alternative would be to calculate the totals for each group twice (which is slower)
acc[seriesName] = Object.assign(Object.create(Object.getPrototypeOf(parent)), parent);
acc[seriesName].parentProvider = () => parent;
return acc;
}, {});
return this.totalsIn;
Expand Down
6 changes: 2 additions & 4 deletions tools/rum/slicer.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,6 @@ async function loadData(scope) {
if (scope === 'year') {
dataChunks.load(await loader.fetchPrevious12Months(endDate));
}

draw();
}

export function updateState() {
Expand Down Expand Up @@ -339,7 +337,7 @@ const io = new IntersectionObserver((entries) => {
elems.incognito.addEventListener('change', async () => {
loader.domainKey = elems.incognito.getAttribute('domainkey');
await loadData(view);
herochart.draw();
draw();
});

herochart.render();
Expand All @@ -353,7 +351,7 @@ const io = new IntersectionObserver((entries) => {
elems.timezoneElement.textContent = timezone;

if (elems.incognito.getAttribute('domainkey')) {
loadData(view);
loadData(view).then(draw);
}

elems.filterInput.addEventListener('input', () => {
Expand Down
39 changes: 26 additions & 13 deletions tools/rum/utils.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
/* helpers */
export function scoreValue(value, ni, poor) {
if (value >= poor) return 'poor';
if (value >= ni) return 'ni';
return 'good';
}

export function isKnownFacet(key) {
return false // TODO: find a better way to filter out non-facet keys
|| key === 'userAgent'
Expand All @@ -30,13 +24,32 @@ export function isKnownFacet(key) {

export function scoreCWV(value, name) {
if (value === undefined || value === null) return null;
const limits = {
lcp: [2500, 4000],
cls: [0.1, 0.25],
inp: [200, 500],
ttfb: [800, 1800],
};
return scoreValue(value, ...limits[name]);
let poor;
let ni;
// this is unrolled on purpose as this method becomes a bottleneck
if (name === 'lcp') {
poor = 4000;
ni = 2500;
}
if (name === 'cls') {
poor = 0.25;
ni = 0.1;
}
if (name === 'inp') {
poor = 500;
ni = 200;
}
if (name === 'ttfb') {
poor = 1800;
ni = 800;
}
if (value >= poor) {
return 'poor';
}
if (value >= ni) {
return 'ni';
}
return 'good';
}
export const UA_KEY = 'userAgent';
export function toHumanReadable(num) {
Expand Down

0 comments on commit 63abe86

Please sign in to comment.