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:
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:
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.