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

core: remove dependency on DevtoolsTimelineModel #5533

Merged
merged 10 commits into from
Jun 28, 2018
Merged

Conversation

patrickhulce
Copy link
Collaborator

@patrickhulce patrickhulce commented Jun 20, 2018

this was actually quite a bit smoother than network records thanks to how little we used it, I've now confirmed that the numbers reported in the mainthread-work-breakdown tests are actually what show up in DevTools for the same trace 🎉

image

image

(we break scripting out into 3 chunks but 215+25+48=288)

taking CNN as an example, we were previously missing ~16s of main thread time, ~5s of JS bootup time. it previously took ~2s to compute the devtools timeline model, we now do the computations we need (twice) in ~200ms.

if (!task.attributableURL || task.attributableURL === 'about:blank') continue;

const timingByGroupId = result.get(task.attributableURL) || {};
const original = timingByGroupId[task.group.id] || 0;
Copy link
Member

Choose a reason for hiding this comment

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

bikeshed: originalTime?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

result.set(eventName, event.selfTime));

for (const task of tasks) {
result.set(task.group.id, (result.get(task.group.id) || 0) + task.selfTime * multiplier);
Copy link
Member

Choose a reason for hiding this comment

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

split second part into a variable(s)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

did the first part

taskToGroup,
taskGroups,
taskNameToGroup,
traceEventNames,
Copy link
Member

Choose a reason for hiding this comment

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

traceEventNames unused?

Copy link
Collaborator Author

@patrickhulce patrickhulce Jun 25, 2018

Choose a reason for hiding this comment

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

yeah I was originally using it to limit the events I had to process in hierarchy but it wasn't a huge perf win, removed

* @see https://cs.chromium.org/chromium/src/third_party/blink/renderer/devtools/front_end/timeline/TimelineUIUtils.js?type=cs&q=_initEventStyles+-f:out+f:devtools&sq=package:chromium&g=0&l=39
*/
const taskGroups = {
ParseHTML: {
Copy link
Member

Choose a reason for hiding this comment

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

Any reason to capitalize first letter?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

💅

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

:) but not really, matching DevTools isn't worth much in a removal of DT deps...


class TraceProcessor {
/**
* @param {LH.TraceEvent} event
Copy link
Member

Choose a reason for hiding this comment

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

we may need some docs here. What is a task and why does it matter vs an event? Should this just be its own class/file?

text: groupIdToName.scriptParseCompile},
];

const tasks = TraceProcessor.getMainThreadTasks(trace.traceEvents);
Copy link
Member

Choose a reason for hiding this comment

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

should this be a computed artifact?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah probably, ☠️ 2️⃣ 🐦 1️⃣ 🗿

.map(evt => {
return {
timestamp: evt.ts / 1000,
datauri: `data:image/jpg;base64,${evt.args.snapshot}`,
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised the compiler doesn't object to accessing snapshot

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

well I added snapshot to traceevent definition :)

Copy link
Member

@brendankenny brendankenny Jun 25, 2018

Choose a reason for hiding this comment

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

I meant because it was optional but looks like the compiler's ok passing undefined into template strings :)

@patrickhulce
Copy link
Collaborator Author

FYI This is the only one open I'd really want to make sure lands in 3.0 this week @paulirish @brendankenny so we can jump on 3rd party analysis :)

Copy link
Member

@paulirish paulirish left a comment

Choose a reason for hiding this comment

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

Nice job on the main-thread-tasks library. It's hot. There's gonna be demand to ship it standalone. ;)

I think you can remove the console-quieter file now, too. Woo!

Some questions and comments below..

'XHR Load': groupIdToName.scripting,
'XHR Ready State Change': groupIdToName.scripting,
/**
* Make sure the traceEventNames keep up with the ones in DevTools
Copy link
Member

Choose a reason for hiding this comment

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

curious.. did you make any updates for new names this time?

i reviewed https://chromium.googlesource.com/chromium/src/+blame/566d3c11bcb0059ec695185808d33bb4474fb4d7/third_party/blink/renderer/devtools/front_end/timeline_model/TimelineModel.js#1156 and only saw one obvious omission aside from some odd crypto ones.
image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Well the whole list is new :) last time we were keying off the display names, I pulled everything I saw that I recognized/looked like it could be substantial

'InvalidateLayout',
'Layout',
'UpdateLayer',
'UpdateLayerTree',
Copy link
Member

Choose a reason for hiding this comment

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

UpdateLayer & UpdateLayerTree belong in paintCompositeRender

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

whoops, done

"duration": 63.04200002551079
"group": "other",
"groupLabel": "Other",
"duration": 177.20099999999977
Copy link
Member

Choose a reason for hiding this comment

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

FWIW i reviewed these totals and compared them against what perf panel reports.
they lgtm!


const timingByGroupId = result.get(task.attributableURL) || {};
const originalTime = timingByGroupId[task.group.id] || 0;
timingByGroupId[task.group.id] = originalTime + task.selfTime * multiplier;
Copy link
Member

Choose a reason for hiding this comment

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

feels weird to apply the multiplier in here.
i know it's another loop, but i'd rather apply the multiplier after we loop over all the tasks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh funny I moved it there because it natural in the computation of time instead of a post-step :)

I don't feel that strongly though, done.

Copy link
Member

Choose a reason for hiding this comment

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

ha, it reads better inlined to me too


for (const task of tasks) {
const originalTime = result.get(task.group.id) || 0;
result.set(task.group.id, originalTime + task.selfTime * multiplier);
Copy link
Member

Choose a reason for hiding this comment

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

same comment about the inlined multiplier.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

MainThreadTasks._computeRecursiveTaskGroup(task);
}

const firstTs = (tasks[0] || {startTime: 0}).startTime;
Copy link
Member

Choose a reason for hiding this comment

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

i've always heard the traceEvents could come out of order, however I know this is more of a case of cross-process events. Perhaps not an issue if everything is within the same thread? We should doublecheck with caseq.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oh yeah, we should make sure to use the sorted events here 👍

Copy link
Member

Choose a reason for hiding this comment

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

oh yeah, we should make sure to use the sorted events here 👍

should this depend on traceOfTab.mainThreadEvents? Then it doesn't need to check if tasks are in the main thread and they're already sorted

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yup that was my plan 👍

}

const firstTs = (tasks[0] || {startTime: 0}).startTime;
for (const task of tasks) {
Copy link
Member

Choose a reason for hiding this comment

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

add comment

Baselining all times to be relative to start of the trace, and in milliseconds

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

'UpdateLayerTree',
],
},
paintCompositeRender: {
Copy link
Member

Choose a reason for hiding this comment

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

I noticed you merged together painting & compositing. I'll survive. 😣

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah trying to reduce noise for newbs, if you're digging into the savings/differences there it's time for perf panel/chrome tracing 😛

Copy link
Member

Choose a reason for hiding this comment

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

WFM. plus, i think this distinction matters more in the 60fps/jank case than pageload.

'FireIdleCallback',
'FireAnimationFrame',
'RunMicrotasks',
'V8.Execute',
Copy link
Member

Choose a reason for hiding this comment

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

add 'v8.evaluateModule'

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

groups[name] = value * multiplier;
totalBootupTime += value * multiplier;
let bootupTimeForURL = 0;
for (const timespanMs of Object.values(timingByGroupId)) {
Copy link
Member

Choose a reason for hiding this comment

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

reduce?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

not since we're moving the multiplier down, otherwise yes :)

* @return {Map<string, Object<string, number>>}
* @param {LH.Artifacts.TaskNode[]} tasks
* @param {number} multiplier
* @return {Map<string, Object<keyof taskGroups, number>>}
Copy link
Member

Choose a reason for hiding this comment

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

if you use keyof in the Object<> type (or anything reasonably complicated, it seems), tsc gets confused and promotes to any, so should use Record<> here (maybe we should just give up on Object and always use Record)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

had to widen it to string anyhow


const timingByGroupId = result.get(task.attributableURL) || {};
const originalTime = timingByGroupId[task.group.id] || 0;
timingByGroupId[task.group.id] = originalTime + task.selfTime * multiplier;
Copy link
Member

Choose a reason for hiding this comment

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

ha, it reads better inlined to me too

* Each task will have its group/classification, start time, end time,
* duration, and self time computed. Each task will potentially have a parent, children, and an
* attributeableURL for the script that was executing/forced this execution.
*/
Copy link
Member

Choose a reason for hiding this comment

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

❤️

MainThreadTasks._computeRecursiveTaskGroup(task);
}

const firstTs = (tasks[0] || {startTime: 0}).startTime;
Copy link
Member

Choose a reason for hiding this comment

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

oh yeah, we should make sure to use the sorted events here 👍

should this depend on traceOfTab.mainThreadEvents? Then it doesn't need to check if tasks are in the main thread and they're already sorted

MainThreadTasks._computeRecursiveTaskGroup(task);
}

const firstTs = (tasks[0] || {startTime: 0}).startTime;
Copy link
Member

Choose a reason for hiding this comment

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

does this need the {startTime: 0} fallback?

@paulirish paulirish added the 3.0 label Jun 27, 2018
@patrickhulce
Copy link
Collaborator Author

@paulirish still requested changes?

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.

LGTM!

break;
}

/** @type {string[]} */
Copy link
Member

Choose a reason for hiding this comment

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

nit: not needed

*
* Each task will have its group/classification, start time, end time,
* duration, and self time computed. Each task will potentially have a parent, children, and an
* attributableURL for the script that was executing/forced this execution.
Copy link
Member

Choose a reason for hiding this comment

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

should this be attributableURLs, plural? What does it mean when there's more than one?

@brendankenny
Copy link
Member

Removes 233 KB (48 KB after gzip) from our bundle!


let taskURLs = [];
switch (task.event.name) {
case 'v8.compile':
Copy link
Member

Choose a reason for hiding this comment

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

can we use the names from task-groups? (or at least add a comment about where these come from)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah I'll add a comment :)

});
});

it('should compute attributableURLs correclty', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

plz spell correctly correctly

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

const baseTs = 1241250325;
const url = s => ({args: {data: {url: s}}});
const stackFrames = f => ({args: {data: {stackTrace: f.map(url => ({url}))}}});

Copy link
Member

Choose a reason for hiding this comment

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

here i made this for you

    /*
    An artistic rendering of the below trace:
    █████████████████████████████TaskA██████████████████████████████████████████████
          ████████████████TaskB███████████████████
               ████EvaluateScript██████
                   █D█
    */

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

thanks 🙏

});

it('should compute parent/child correctly', async () => {
const traceEvents = [
Copy link
Member

Choose a reason for hiding this comment

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

here i made this for you:

    /*
    An artistic rendering of the below trace:
    █████████████████████████████TaskA██████████████████████████████████████████████
          ████████████████TaskB███████████████████
               ███████TaskC██████████
    */

i also made an x-axis but it scale correctly against the box-drawing characters in any monospace fonts except the one i personally use. :(

    |----|-----|---|---|------------------|-----------------|--------------------------------------------|-
    0    5     10  15  20                 40                55                                           100 

@paulirish
Copy link
Member

:shipit:

@patrickhulce patrickhulce merged commit 341f0a8 into master Jun 28, 2018
@patrickhulce patrickhulce deleted the remove_dtm_dep branch June 28, 2018 17:53
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.

None yet

3 participants