WWDC2019 Session 513

Transcript

[ Music ]
>> Hello, I'm Jonathan Davis,
Web Technologies Evangelist for
the Safari and WebKit Teams.
Welcome to "Understanding CPU
Usage with Web Inspector."
We all know that battery life is
important to users, and you
probably instinctively know the
web browser is one of the
most-used apps on our devices.
But beyond that, web content is
also incorporated into many
popular iOS and macOS apps.
That means web content
significantly affects battery
life for users, so making it
power-efficient has a big
impact.
Safari and WebKit already
provide features to
automatically save power for
users when possible, such as
throttling timers when a webpage
is in the background.
And with support for content
blocker extensions, users can
automatically block loading
unwanted content that's often
just junk slowing down their
browser, or worse, tracking
them.
But even with the built-in
power-saving features in Safari,
there's a lot left in the hands
of web developers, so it's an
opportunity for all of us to
improve performance and build a
power-efficient web for users.
The good news is that everything
you know about good performance
practices on the web also apply
as best practices for saving
power.
So all of the things we've heard
about improving page load time,
optimizing JavaScript, and using
CSS animations and transitions
really help with battery life.
Whether you're experienced with
all of the best practices for
performance on the web, or just
starting out, I'm going to show
you a powerful new tool in Web
Inspector that's going to give
you super powers for finding
where performance can be
improved to save battery life.
It's called the CPU Usage
Timeline, and it's new in Web
Inspector in Safari 13 for
macOS.
It provides a window into power
use that makes it easy to see
the impact of behaviors that
contribute to high CPU usage.
Today, I'm going to show you how
to work with the new CPU Usage
Timeline, discover power issues
through CPU Usage, and then I'll
briefly go over some strategies
for improving power savings in
your web-based projects.
Now, before you can use the new
timeline, you'll need to enable
the Develop menu to access Web
Inspector.
Just load up Safari Preferences,
and click on Advanced, and click
to enable to Show Develop menu
in menu bar option.
Now, just load up a webpage, and
open Web Inspector from the
Develop menu, or use the
keyboard shortcut
Command-Option-I.
I'm going to use the new CPU
Usage Timeline to look for where
we can improve power on
webkit.org.
The homepage is pretty simple,
but it should be interesting,
because it still has some
dynamic things happening, like
this subtle logo animation in
the background.
With Web Inspector open, click
over to the Timelines tab.
In the upper left is a list of
different timelines.
There's a bunch of other
timelines you can use, but the
defaults will work great to look
for ways to improve CPU usage.
To start a recording, you can
click the red Record button, or
press the Space bar, but I'm
going to click the Reload
button, which will automatically
start a recording and capture
page load, too.
When looking at CPU usage, it's
important to remember you'll
want to record at least 15
seconds in order to get useful
measurements.
So I'll let this recording go a
bit beyond that to capture
enough data.
Now I'll scroll over the
timeline to zoom out a bit so I
can see everything.
Clicking on the CPU timeline,
you can see details about CPU
usage of the page.
At a glance, I can tell the page
does a pretty good job of
keeping energy use low.
The quick load time helps a lot,
and when the page becomes idle,
the timeline shows the page is
basically doing nothing.
This is great, because it means
users can stay on the page all
day, and the CPU drops to a very
low power state, with negligible
battery drain caused by the web
content.
The main thread is where a lot
of interesting work happens for
web content.
The main thread chart shows
different categories of work
done on the main thread,
including JavaScript processing,
painting, layout, and things in
service of layout, like style
recalculation.
Based on this, we can see that
most of the time working was
spent on painting.
In the middle of the indicator
is the time it took for the main
thread to complete its work.
The entire recording was about
20 seconds, but the main thread
only took about 100 milliseconds
to complete its work.
Below the main thread chart is a
detailed breakdown of the CPU
usage across all of the threads
involved in doing work for the
web content.
I can take a closer look at this
by clicking and dragging in the
timeline to select just the time
range where the work is being
done.
And just under the CPU usage
graph, in this strip is the main
thread indicator, which shows
categories of work that were
happening on the main thread at
different points in time.
In this area of growing
activity, there were some layout
events in red, followed by
significant painting work, just
like in the chart above, but
showing when it happened.
The energy impact gauge really
brings it all together, though.
It provides a score based on the
total average usage across all
CPU cores for the selected time
range.
The energy impact gauge is
interactive, and as a total
average, will change depending
on the time range selected.
For the page load period, the
gauge shows medium energy
impact, but loading is expensive
in terms of power, so that's
expected.
At least this stayed out of the
high range.
That would've indicated a
problem but double-clicking the
timeline area to select the
entire recording shows the
overall average CPU is low.
I can click and drag the
timeline to select a slice of
time out of the recording and
watch the average change.
Now, when I click to grab the
selection, and slide it to a
period where the page goes idle,
the energy impact drops to low.
That's really great.
When the page is idle, we don't
want to be costing the user lots
of power for content they're not
actively using.
The CPU use was in the low range
while idle, even with the
rotating logo animation
happening in the background.
That's because it's using a
simple CSS animation that makes
it really cheap to animate, but
it still gives the page a nice
visual impact.
Webkit.org is doing great so
far, but when you're looking for
power issues, looking at loading
and idle are good starts.
To cover all of the bases, we
need to capture some
interactivity, too.
The WebKit Feature Status page
is a great way to stay up to
date on WebKit support for your
favorite web features.
It allows you to filter and
search, and that's perfect for
capturing interaction.
I'm going to record a timeline
and interact with the page in
some ways.
I'll just scroll down to the
bottom, and then all the way
back up to the top, and then
I'll do a quick search, and
click on some things here and
there.
And again, we should at least
capture 15 seconds.
Since we're on a new page, we
should capture some period of
idle, too.
I'm going to let this continue
recording to get enough idle
time captured.
Okay, that looks pretty good.
Now I'm going to select the
entire recording and scroll to
zoom out of the timeline a bit
so we can see everything and
click to look at the CPU usage
details.
Let's start digging into these
areas, starting with loading.
The page managed to stay out of
the high range during the quick
load time, so we're okay there.
Now let's look at this range of
interactivity.
The energy impact stays in the
medium range, even with all of
these things happening, but you
can tell from the JavaScript in
events timeline there's a lot of
script firing while scrolling
and interacting.
We definitely want to avoid
doing any extra CPU work while
scrolling, since it's very
expensive to scroll.
Let's see if we need to be doing
that work or not.
The Statistics and Sources
sections tell the story.
There were over 1200 entries
into JavaScript for the selected
time in the recording, 594
request animation frame timers
fired, and there are 647 scroll
events.
Now, I know that reducing timers
and staying off of scrolling is
better for performance, but we
need to look at the code to know
if it's necessary or can be
improved.
In the Statistics area, you can
click on the timers or events to
filter the sources on the right
to the code that triggered it,
and clicking the source takes
you right to the code in the
JavaScript Debugger.
Ah, OK, it looks like the
request animation frame timer is
calling updateImages, which
iterates over all of the images,
and checks if they are in view,
and if so, it loads them.
It's a pretty basic, lazy-loaded
images pattern, but this
dimmed-out code in the debugger
tells us inView and loadImage
are never called.
Even though we scrolled the
entire page to the bottom, and
back up to the top, it makes
sense, though, because there's
basically no images on the page.
So the lazy-loading image code
shouldn't be firing at all on
this page.
Let's switch back to the code.
The images and the event
handlers are set up here.
So looking at the code, it's
just always setting up the event
handlers for scroll and resize
all of the time.
To keep that from happening, we
just need to add a conditional
guard.
There, let's try that.
OK, so let's capture a timeline
with the fix in place.
I'll click back over to the
Timelines tab, start a new
recording, and I'll do some
quick scrolling.
You can already see there are no
JavaScript entries showing up in
the timeline.
It's all just paint, so our
guard is working to prevent
extra JavaScript work while
scrolling on a page without any
lazy-loaded images.
The energy impact has been
reduced, and all of the main
thread work is paints for
scrolling, but we need to make
sure that the pages that should
be using lazy-loaded images are
still working.
Again, with the fix in place,
I'll record a new timeline on a
page with a MotionMark logo
image at the bottom that should
use the lazy loader.
Perfect. The MotionMark image
loaded in.
I'm going to scroll to get some
CPU measurements of the new
behavior and click the CPU
Timelines to see where we're at.
So we've reduced the timer and
events to just the pages where
it's necessary.
That's great, but there's still
a lot of entries into Script
when scrolling on pages with a
lazy-loaded image.
There's an API available in
Safari that we can use to take
this solution a step further.
We can replace the request
animation frame implementation
to use Intersection Observer
instead.
Intersection Observer can tell
when an element comes into view,
so you can limit your work to
just when it's visible.
And as soon as it goes out of
view, you can stop the work to
get back to a low CPU power
state and save power.
So with the Intersection
Observer implementation, let's
record another timeline, and do
some scrolling to see how this
solution performs.
And as I begin scrolling,
there's just a single entry into
JavaScript, and for the rest of
the time, it's just painting
while I scroll.
So in the end, we went from
16.3% down to 9-1/2% average CPU
usage with the Intersection
Observer solution.
Every little bit helps, and
that's a really nice
improvement.
So there are lots of ways to
save power in web content.
Use the CPU usage timeline to
investigate web content for ways
to improve during interactivity
and when the page is idle.
Remember that reducing CPU usage
saves energy and reduces battery
drain for users.
CSS animations and transitions
can provide dynamic visuals
without the power cost and try
to avoid doing work while
scrolling.
Using the Intersection Observer
API instead.
Users want an engaging, dynamic
experience with web content, but
the best version of those
experiences use the least amount
of CPU possible.
There are a lot more
power-saving tips for web
development on the WebKit Blog.
Check out the links in the
resources section associated
with this video.
We hope you'll try using these
tools on your web content, and
use the advice in this video as
some starting places for your
investigations on how to make
your web content power
efficient, and join us in
helping the web become more
powerful by using less power.