CPU time breakdown


#1

Network was the critical performance bottleneck 10 years, but today it’s CPU, especially given the amount of mobile traffic on lesser-powered devices. WebPageTest has awesome CPU data so I created some pie charts.

Here’s the query for medians on desktop:

SELECT count(*) as numsites,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.BlinkGC.AtomicPhase']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS BlinkGC_AtomicPhase,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.CommitLoad']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS CommitLoad,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.EvaluateScript']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS EvaluateScript,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.EventDispatch']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS EventDispatch,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.FireAnimationFrame']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS FireAnimationFrame,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.FireIdleCallback']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS FireIdleCallback,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.FunctionCall']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS FunctionCall,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.HitTest']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS HitTest,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.Idle']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS Idle,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.Layout']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS Layout,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.MajorGC']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS MajorGC,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.MarkDOMContent']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS MarkDOMContent,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.MarkLoad']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS MarkLoad,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.MinorGC']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS MinorGC,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.Paint']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS Paint,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.ParseAuthorStyleSheet']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS ParseAuthorStyleSheet,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.ParseHTML']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS ParseHTML,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.PlatformResourceSendRequest']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS PlatformResourceSendRequest,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.ResourceChangePriority']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS ResourceChangePriority,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.ResourceSendRequest']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS ResourceSendRequest,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.ScriptWrappableMarkingVisitor']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS ScriptWrappableMarkingVisitor,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.ScrollLayer']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS ScrollLayer,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.TimerFire']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS TimerFire,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.UpdateLayerTree']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS UpdateLayerTree,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.UpdateLayoutTree']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS UpdateLayoutTree,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.XHRLoad']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS XHRLoad,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.XHRReadyStateChange']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS XHRReadyStateChange,
APPROX_QUANTILES( CAST( IFNULL(JSON_EXTRACT(payload, "$['_cpu.v8.compile']"),'0') as INT64), 1001)[SAFE_ORDINAL(501)] AS v8_compile

FROM `httparchive.pages.2018_10_01_desktop`

I’m not sure if the list of CPU tasks is complete. I created it by looking at ~10 HAR files and taking the union. I had a hard time finding explanations for each task, and am still uncertain today. The best links I found are Timeline Event Reference and this blink-dev thread Main thread attribution for top million sites.

I exported the results from this query to Google Spreadsheets and did a pie chart:

I color-coded the tasks for JS (orange), Layout (purple), Paint (green), and Load (blue). The total of JS time is 433ms at the median. I ran similar queries for 95th and 99th percentile (change “501” to “951” and “991” respectively), and also ran them for mobile (change “desktop” to “mobile”). Here are the results:

image

Then I created this fun visual to compare all the pie charts where the size of the pie chart is relative to the JS CPU time:
image

That graphic is mostly for looks, but there is one trend worth noting. As pages get slower, JS is the CPU task that grows the most. When it comes to web performance there are three words to remember: CPU, CPU, and CPU.


#2

In the last graph, shouldn’t the pie chart size be the square root of total CPU time, so the area relation is maintained?


#3

Yes, exactly! “Size” means area.


#4

The stacked pie charts are an interesting visual experiment. Have you tried showing that data as vertical stacked bar graphs, so the growth/decline in certain in areas can be seen? Height of the bar graph could be what “size” represents in the pie charts.

As parsing/compilling now happens more and more off-thread (has been for a long time in Firefox); including main thread idle time also becomes an interesting measure – as not only network can cause the main thread to wait for data but also script compilation.