Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

report: reuse numberFormatters for ~50% performance gains #14493

Merged
merged 2 commits into from
Nov 29, 2022

Conversation

paulirish
Copy link
Member

@paulirish paulirish commented Nov 3, 2022

deferring table rendering stuff that we are _deferring_ to another PR

baseline perf of renderReport.. ~140ms JS plus rendering:
image

after deferring renderTable's impl.. just 70ms of JS before FCP:
image

and then

reusing formatters brings 120ms of table rendering down to 50ms:
image

that is renderReport being 50% faster!!!!!!!

return this._renderTable(details);
{
// Defer rendering of hidden tables, for a faster FCP
const tableElem = this._dom.createElement('table', 'lh-table');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no way this didnt break tests

return new Intl.NumberFormat(this._locale, opts).format(number).replace(' ', NBSP2);
let formatter;
// eslint-disable-next-line max-len
const cacheKey = `${opts.minimumFractionDigits}${opts.maximumFractionDigits}${opts.style}${opts.unit}${opts.unitDisplay}`;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe an array + join('')?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like this should really be a thing in js by now.

Could also do JSON.stringify(Object.entries(opts).sort(([keyA], [keyB]) => keyA.localeCompare(keyB))) (and throw in locale at the end or whatever)

return new Intl.NumberFormat(this._locale, opts).format(number).replace(' ', NBSP2);
let formatter;
// eslint-disable-next-line max-len
const cacheKey = `${opts.minimumFractionDigits}${opts.maximumFractionDigits}${opts.style}${opts.unit}${opts.unitDisplay}`;
Copy link
Collaborator

@connorjclark connorjclark Nov 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems safe to not include locale in the key, but maybe we might as well? to protect against a future refactor

Copy link
Collaborator

@connorjclark connorjclark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: caching the formatter, nice! IIRC the hope was "surely v8 would do this for us". Should we file a crbug for them?

@brendankenny
Copy link
Member

v8 has only ever cached the simplest formatters (e.g. the default toLocaleString without options). They've added a few cases beyond that, but the advice has always been to reuse formatters with complicated options. Existing bug: https://crbug.com/v8/11159

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

50ms still seems slow for table rendering, honestly :/

return new Intl.NumberFormat(this._locale, opts).format(number).replace(' ', NBSP2);
let formatter;
// eslint-disable-next-line max-len
const cacheKey = `${opts.minimumFractionDigits}${opts.maximumFractionDigits}${opts.style}${opts.unit}${opts.unitDisplay}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like this should really be a thing in js by now.

Could also do JSON.stringify(Object.entries(opts).sort(([keyA], [keyB]) => keyA.localeCompare(keyB))) (and throw in locale at the end or whatever)

@@ -53,7 +53,12 @@ export class DetailsRenderer {
return this._renderList(details);
case 'table':
case 'opportunity':
return this._renderTable(details);
{
// Defer rendering of hidden tables, for a faster FCP
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really good idea in general, but feels like there could be a more principled way of doing it. What about deferring all audit detail rendering to a second pass and triggering it up in renderReport()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. We probably need a special case for filmstrip (and maybe *-budgets) but.. otherwise all of detailsrenderer deferred seems cool.

@kaled336

This comment was marked as spam.

@connorjclark connorjclark added 10.0 and removed 10.0 labels Nov 28, 2022
@paulirish paulirish changed the title report(perf): defer renderTable and reuse numberFormatters report(perf): reuse numberFormatters Nov 29, 2022
@connorjclark connorjclark changed the title report(perf): reuse numberFormatters report: reuse numberFormatters for ~50% performance gains Nov 29, 2022
@connorjclark connorjclark merged commit 046bdd1 into main Nov 29, 2022
@connorjclark connorjclark deleted the faster-render-report branch November 29, 2022 22:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants