-
Notifications
You must be signed in to change notification settings - Fork 400
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
Conversation
// pieces of the queue are still pending to be rehydrated, those should have priority | ||
if (rehydrateQueue.length === 0) { | ||
addCallbackToNextTick(flushRehydrationQueue); | ||
startGlobalMeasure('rehydrate'); |
There was a problem hiding this comment.
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');
}
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this 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.
@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). Not sure about the implementation but the abstractions and where to put the marks have been reviewed by Pierre and I. |
There was a problem hiding this 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 = |
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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
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"; |
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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.
entry point.
1bf3645
to
2245cbb
Compare
@tariq-sfdc this pr is intended to add these 3 markers in prod: |
This comment has been minimized.
This comment has been minimized.
@jodarove that's what I mean. What does the timeline look like when you run in |
here is a chrome profile of PROD @tariq-sfdc |
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
This comment has been minimized.
This comment has been minimized.
|
||
export function buildCustomElementConstructor(Ctor: ComponentConstructor, options?: ShadowRootInit): HTMLElementConstructor { | ||
startGlobalMeasure(GlobalMeasurementPhase.INIT); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this 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', |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also @Gr8Gatsby
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple ideas:
DiffAndPatch
Patch
Render
The markers were added in the incorrect place when building a wc.
Benchmark resultsBase commit: lwc-engine-benchmark
|
There was a problem hiding this 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
@caridy yes, i already talked with @kevinv11n . he will jump when has a chance 👍 |
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? |
@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 |
There was a problem hiding this 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
@diervo we don't plan to polyfill performance.mark / measure. We're not using PeformanceObserver in chrome either. |
@tariq-sfdc tell me more :) |
@diervo check pm comment on that: https://github.com/salesforce/lwc/blob/master/packages/lwc-engine/src/framework/performance-timing.ts#L12 |
what's left to do here ? |
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.
|
@tariq-sfdc every commit now needs to be merged in a timely manner and test the hell out of it, so little by little :) |
@Gr8Gatsby I'm fine with those names. render and rehydrate are sometime a subset of the other during insertion. |
I want to do a final pass today before merging please. |
This reverts commit 24e029a. # Conflicts: # packages/lwc-engine/src/framework/upgrade.ts # packages/lwc-engine/src/framework/vm.ts
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?