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

feat(engine): performance marks #756

Merged
merged 5 commits into from
Nov 2, 2018
Merged

Conversation

jodarove
Copy link
Contributor

@jodarove jodarove commented Oct 22, 2018

We need a way of measuring the time that lwc code is running in prod.

With that in mind, this pr is to add 3 global markers:

  • lwc-init which is the measure time creating a lwc root component.
  • lwc-hydrate to measure how long took hydrating all dependencies of the root component.
  • lwc-rehydrate to measure how long took to flush all scheduled rehydrations.

Does this PR introduce a breaking change?

  • Yes
  • No

// pieces of the queue are still pending to be rehydrated, those should have priority
if (rehydrateQueue.length === 0) {
addCallbackToNextTick(flushRehydrationQueue);
startGlobalMeasure('rehydrate');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the only actual change here was to wrap everything in a try/finally block:

    // ... js code ...

to:

    startGobalMeasure('rehydrate');
    try {
        // ... js code ...
    } finally {
         endGlobalMeasure('rehydrate');
    }

Copy link
Contributor

Choose a reason for hiding this comment

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

I would rather we wrapped the code calling flushRehydrationQueue with a try...catch if we must. This function is already big enough as is so keeping the measures outside would be great if we can. Also, do these measurements need to run in production? Having an extra try...catch will be a perf hit

Copy link
Contributor

@caridy caridy Oct 23, 2018

Choose a reason for hiding this comment

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

yes, definitely not a try catch here, it will break error boundaries, and other stuff that depend on this block throwing an error. @davidturissini suggestion is tricky because it is the next tick.

I believe there is room here to add the measurements in this method, but without adding the try/catch, just rely on the existing one.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm also fine with this name btw.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i agree with @caridy in both things: (1) the try/finally outside is tricky cause i in the nextTickQueue but (2) we would need to make the measure before throw inside the catch https://github.com/salesforce/lwc/blob/master/packages/lwc-engine/src/framework/vm.ts#L335

going with (2) since solves all the issues.

@jodarove jodarove requested a review from diervo October 22, 2018 18:49
@salesforce-best-lwc-internal

This comment has been minimized.

Copy link
Contributor

@caridy caridy left a comment

Choose a reason for hiding this comment

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

I don't agree with this measurements. Honestly, I don't understand how these measurements are useful to anyone. On top of that, there is no clear picture here on which elements do you want to measure.

@diervo
Copy link
Contributor

diervo commented Oct 23, 2018

@caridy I suggest you ask for context before stating that you don't understand anything.

This is fundamentally to understand when LWC is doing work (both creating and rendering).
It is both critical and necessary for perf that they can disambiguate between idle time and LWC time.

Not sure about the implementation but the abstractions and where to put the marks have been reviewed by Pierre and I.

Copy link
Contributor

@davidturissini davidturissini left a comment

Choose a reason for hiding this comment

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

Couple changes

@@ -9,6 +9,11 @@ type MeasurementPhase =
| 'renderedCallback'
| 'errorCallback';

type GlobalMeasurementPhase =
Copy link
Contributor

Choose a reason for hiding this comment

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

Make this an enum

// pieces of the queue are still pending to be rehydrated, those should have priority
if (rehydrateQueue.length === 0) {
addCallbackToNextTick(flushRehydrationQueue);
startGlobalMeasure('rehydrate');
Copy link
Contributor

Choose a reason for hiding this comment

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

I would rather we wrapped the code calling flushRehydrationQueue with a try...catch if we must. This function is already big enough as is so keeping the measures outside would be great if we can. Also, do these measurements need to run in production? Having an extra try...catch will be a perf hit

@caridy
Copy link
Contributor

caridy commented Oct 23, 2018

I had a chat with Diego about this, will add more comments now that I have more context, but I continue to ask for more information as part of the PR, the description of the PR says little, don't link to any doc, and doesn't explain the why.

@@ -8,6 +8,7 @@ import { isNativeShadowRootAvailable } from "./dom-api";
import { patchCustomElementProto } from "./patch";
import { getComponentDef, setElementProto } from "./def";
import { patchCustomElementWithRestrictions } from "./restrictions";
import { endGlobalMeasure, startGlobalMeasure } from "./performance-timing";
Copy link
Contributor

@caridy caridy Oct 23, 2018

Choose a reason for hiding this comment

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

upgrade.ts and wc.ts are analog. In aura, we always go thru upgrade.ts, but for web components, we go thru wc.ts, probably worth to keep them analog by implementing the same measurements.

@@ -59,6 +60,7 @@ assign(Node.prototype, {
* then it throws a TypeError.
*/
export function createElement(sel: string, options: any = {}): HTMLElement {
startGlobalMeasure('createElement');
Copy link
Contributor

@caridy caridy Oct 23, 2018

Choose a reason for hiding this comment

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

I will recommend to change the name of this because createElement as a very specific meaning. I will recommend to keep it shorted as init which includes the createElement + the initialization of the associated components, and everything in between.

Copy link
Contributor

Choose a reason for hiding this comment

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

So, in the case of wc.ts, ths measurement are a little bit different, but still having an init phase is good enough.

@@ -94,14 +96,17 @@ export function createElement(sel: string, options: any = {}): HTMLElement {
createVM(sel, element, Ctor, { mode, fallback, isRoot: true });
// Handle insertion and removal from the DOM manually
setInternalField(element, ConnectingSlot, () => {
startGlobalMeasure('connectingRootElement');
Copy link
Contributor

Choose a reason for hiding this comment

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

I will recommend renaming this as well because the connection aspect is just an implementation detail. I will call this hydrate

Adds performance marks for:

- createElement
- render/patch generated from connecting a root element.
- flush rehydrate queue.
- enum
- init instead of createElement
- hydrate instead of connectedRootElement.
@jodarove jodarove force-pushed the jodarove/performance-markers branch from 1bf3645 to 2245cbb Compare October 24, 2018 20:57
@jodarove
Copy link
Contributor Author

markers with the comments:

image

image

@tariqrafique
Copy link
Contributor

markers with the comments:

image

image

Is that from running in prod mode ?

@jodarove
Copy link
Contributor Author

@tariq-sfdc this pr is intended to add these 3 markers in prod: lwc-init, lwc-hydrate and lwc-rehydrate (not the individual ones)

@salesforce-best-lwc-internal

This comment has been minimized.

@tariqrafique
Copy link
Contributor

@jodarove that's what I mean. What does the timeline look like when you run in PROD mode ?

@jodarove
Copy link
Contributor Author

here is a chrome profile of PROD @tariq-sfdc

attach-event-connected-callback-profile.json.zip

@tariqrafique
Copy link
Contributor

profile looks good. Thanks @jodarove

function calls:

before in each call to start/endGlobalMeasure the condition !isUserTimingSupported was being executed. But isUserTimingSupported is calculated once when importing the module.

Now when isUserTimingSupported is calculated if is true we export the start/endGlobalMeasure without the condition, and if ! isUserTimingSupported, we export a noop function as start/endGlobalMeasure
@salesforce-best-lwc-internal

This comment has been minimized.


export function buildCustomElementConstructor(Ctor: ComponentConstructor, options?: ShadowRootInit): HTMLElementConstructor {
startGlobalMeasure(GlobalMeasurementPhase.INIT);
Copy link
Contributor

Choose a reason for hiding this comment

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

no, this goes into the constructor, first line before super() call

@@ -70,4 +74,7 @@ export function buildCustomElementConstructor(Ctor: ComponentConstructor, option
// the reflection from attributes to props via attributeChangedCallback.
static observedAttributes = ArrayMap.call(getOwnPropertyNames(props), (propName) => props[propName].attr);
};

endGlobalMeasure(GlobalMeasurementPhase.INIT);
Copy link
Contributor

Choose a reason for hiding this comment

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

this goes inside the constructor, last line.

Copy link
Contributor

@caridy caridy left a comment

Choose a reason for hiding this comment

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

wrong placement in wc.ts

@@ -9,6 +9,12 @@ type MeasurementPhase =
| 'renderedCallback'
| 'errorCallback';

export enum GlobalMeasurementPhase {
REHYDRATE = 'rehydrate',
Copy link
Contributor

Choose a reason for hiding this comment

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

still think we need better names :) but I don't know... talk to @kevinv11n

Copy link
Contributor

Choose a reason for hiding this comment

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

also @Gr8Gatsby

Copy link
Contributor

Choose a reason for hiding this comment

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

A couple ideas:

  1. DiffAndPatch
  2. Patch
  3. Render

The markers were added in the incorrect place when building a wc.
@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: 00a13cc | Target commit: d8a1a27

lwc-engine-benchmark

table-append-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table/append/1k duration 156.25 (±4.80 ms) 149.50 (±3.55 ms) -6.8ms (4.3%) 👍
table-clear-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table/clear/1k duration 6.60 (±0.30 ms) 6.00 (±0.30 ms) -0.6ms (9.1%) 👍
table-create-10k metric base(00a13cc) target(d8a1a27) trend
benchmark-table/create/10k duration 879.85 (±5.55 ms) 867.95 (±5.60 ms) -11.9ms (1.4%) 👍
table-create-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table/create/1k duration 114.90 (±2.40 ms) 113.15 (±2.60 ms) -1.8ms (1.5%) 👌
table-update-10th-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table/update-10th/1k duration 86.05 (±3.00 ms) 78.55 (±4.00 ms) -7.5ms (8.7%) 👍
tablecmp-append-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table-component/append/1k duration 215.90 (±13.20 ms) 220.90 (±16.05 ms) +5.0ms (2.3%) 👌
tablecmp-clear-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table-component/clear/1k duration 11.40 (±1.30 ms) 11.60 (±1.65 ms) +0.2ms (1.8%) 👌
tablecmp-create-10k metric base(00a13cc) target(d8a1a27) trend
benchmark-table-component/create/10k duration 1656.90 (±12.90 ms) 1652.55 (±9.45 ms) -4.3ms (0.3%) 👌
tablecmp-create-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table-component/create/1k duration 198.90 (±4.80 ms) 200.25 (±5.95 ms) +1.4ms (0.7%) 👌
tablecmp-update-10th-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table-component/update-10th/1k duration 69.35 (±4.25 ms) 71.95 (±3.55 ms) +2.6ms (3.7%) 👌
wc-append-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table-wc/append/1k duration 231.25 (±9.40 ms) 260.90 (±11.95 ms) +29.6ms (12.8%) 👎
wc-clear-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table-wc/clear/1k duration 22.15 (±2.55 ms) 21.05 (±2.20 ms) -1.1ms (5.0%) 👌
wc-create-10k metric base(00a13cc) target(d8a1a27) trend
benchmark-table-wc/create/10k duration 1685.95 (±52.35 ms) 3857.80 (±19.25 ms) +2171.9ms (128.8%) 👎
wc-create-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table-wc/create/1k duration 206.80 (±5.20 ms) 231.25 (±4.70 ms) +24.4ms (11.8%) 👎
wc-update-10th-1k metric base(00a13cc) target(d8a1a27) trend
benchmark-table-wc/update-10th/1k duration 75.05 (±6.25 ms) 77.00 (±6.15 ms) +2.0ms (2.6%) 👌

Copy link
Contributor

@caridy caridy left a comment

Choose a reason for hiding this comment

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

still think u should talk to others about the names

@jodarove
Copy link
Contributor Author

@caridy yes, i already talked with @kevinv11n . he will jump when has a chance 👍

@dbajric
Copy link
Contributor

dbajric commented Oct 26, 2018

The comment I will make here is for existing code, but it is to consider to add into this PR.

The startMeasure and endMeasure functions are exported directly and on every call they check if user timing is supported. I like what you've done with startGlobalMeasure and endGlobalMeasure where you return noop function if user timing is not supported. Can we do the same for startMeasure and endMeasure?

@diervo
Copy link
Contributor

diervo commented Oct 26, 2018

@dbajric I dont think we actually even need any of that. @tariq-sfdc and @nolanlawson are working on the polyfill which will endup in COMPAT. Hence I dont think we need noops

Copy link
Contributor

@diervo diervo left a comment

Choose a reason for hiding this comment

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

Lets talk to the perf team see if we really need the noops

@tariqrafique
Copy link
Contributor

@diervo we don't plan to polyfill performance.mark / measure. We're not using PeformanceObserver in chrome either.

@diervo
Copy link
Contributor

diervo commented Oct 26, 2018

@tariq-sfdc tell me more :)

@jodarove
Copy link
Contributor Author

jodarove commented Oct 26, 2018

@tariqrafique
Copy link
Contributor

tariqrafique commented Oct 26, 2018

@diervo you reviewed and approved the metrics service changes to listen for performance mark/measure: internal link removed by @caridy

@tariqrafique
Copy link
Contributor

what's left to do here ?

@Gr8Gatsby
Copy link

Gr8Gatsby commented Oct 31, 2018

@caridy @kevinv11n

For this pull request we should use lwc-render since this is what is happening, lwc-rehydrate can be easily confused for future generations, as rehydration generally means something was pulled from a cache and re-added to the DOM in this case. These definitions make more sense to me, feel free to correct my understanding, but render works.

lwc-init - The engine has added a root LWC element and called it's constructor.
lwc-hydrate - The engine has completed the connectedCallback for the root LWC element.
lwc-render - The engine has rendered or re-rendered component(s)
lwc-destroy - The engine has removed the root element meaning disconnectedCallback was completed for the root

@diervo
Copy link
Contributor

diervo commented Nov 1, 2018

@tariq-sfdc every commit now needs to be merged in a timely manner and test the hell out of it, so little by little :)
🤷‍♂️

@caridy
Copy link
Contributor

caridy commented Nov 1, 2018

@Gr8Gatsby I'm fine with those names. render and rehydrate are sometime a subset of the other during insertion.

@kevinv11n
Copy link
Contributor

:shipit:

@diervo
Copy link
Contributor

diervo commented Nov 1, 2018

I want to do a final pass today before merging please.
I will merge this after I have done so

@diervo diervo merged commit 24e029a into master Nov 2, 2018
@diervo diervo deleted the jodarove/performance-markers branch November 2, 2018 06:13
jodarove added a commit that referenced this pull request Nov 7, 2018
This reverts commit 24e029a.

# Conflicts:
#	packages/lwc-engine/src/framework/upgrade.ts
#	packages/lwc-engine/src/framework/vm.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants