Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
[ Music ]
>> Good morning.
Welcome to "Improving Power
Efficiency with AppNap."
My name is Tony Parker and
I'm a software engineer
on the Cocoa Frameworks
Team at Apple.
So today we're going to
go over three topics.
First, what is AppNap?
Then we're going
to go into a lot
of detail on how AppNap works.
And finally we're going to
cover AppNap and other power
and energy related API.
So first let's talk
about what AppNap is.
So we live in a world
today on OS X
where users expect
both long battery life
and at the same time
high performance
responsive applications.
However, because
it's a multitasking,
multi user operating system,
all apps actually have
about equal access to limited
resources like CPU time, disk IO
and most importantly
for this talk, energy.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So in designing this feature, we
wanted to focus system resources
on the most important user work.
And the benefit of that is going
to be increased battery life
and improved responsiveness.
Now the tricky part of
course is deciding what
that important work is and we do
that with a set of heuristics.
So for example, we look at
whether an application is
in the foreground
- typically the one
with the menu bar -
or the background.
Typically a foreground
application is doing more
important user work.
We also look at the
application type.
So a system daemon
or a background agent is
typically doing less important
work than an application that
the user can actually sort
of see and interact with
on the doc for example.
We can also take into
account the visibility
of your application's
windows on screen.
So an application that has a
window that's completely hidden
by other windows or on another
space that's been moved away is
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
typically doing less
important work to the user
than the ones it can see
right in front of them.
And also we look at
drawing activity.
So an application
that can update it --
it's updating itself and
displaying new content
to the user, that
might be something
that the user is looking
at and actively watching.
And that would indicate
more important work.
Now even if an application
is completely hidden,
if it's playing audio, that's
obviously also perceptible
to the user so we
count that as well.
And of course, event processing.
User events and events like
hot keys are also considered.
We can also use the existing
I/O Kit Power Searching API.
You might be familiar with this.
It's been on OS X for a while
and it lets you tell
the system you know,
"Don't put the system -- don't
allow the system to idle sleep
because I'm doing something
that I need to finish."
And finally we have a
new set of AppNap APIs
which let you help improve these
heuristics by telling the system
about the kinds of
activities you're doing.
So I want to show you a quick
demo of AppNap in action.
Okay, so here we go.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you've ever been to a
presentation like this one
and had trouble following what
a presenter like me is doing,
I've got the App here for you.
And it may also be familiar
to some of you old timers.
As you can see, these apps --
they're 2 copies
of the same app.
It's following my mouse as I
move it around the screen here.
And I also have Activity
Monitor open.
And I want to show you a
couple new things we've added
to Activity Monitor that can
really help users understand
where their energy is going.
So there are 2 columns
in particular I want
to focus on here.
First, the AppNap column.
Now you can see that both
of these applications are
-- say "No" right now.
In fact, one of these
applications is completely
unmodified and the other
one I've explicitly modified
to prevent AppNap.
The other column is
the energy impact.
Now you notice that this
column is unitless and that's
because it's really sort of an
aggregate score of the kinds
of things an application can do
that can cause battery drain.
So I mentioned that visibility
is one of the heuristics
that we use for determining
eligibility for AppNap.
So what I'm going to do is
move my Activity Monitor window
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in front of these other
two windows and we'll see
after a few seconds the
system will decide that one
of these apps - the one
that was unmodified -
can go into that AppNap state.
And that's going to
reduce its energy impact
to a much lower level.
In fact here you
see it's at zero.
However, if I just - you
know - move my window
out of the way a little
bit, you see both windows --
or both applications still
appear responsive to the user.
And so the idea here is to
not interrupt user's work
but instead focus in this
case the battery life
on the applications that
are doing important work.
And we'll see what a difference
this makes in a little bit.
Now let's go back to our slides.
So let's go into some
detail on how all
of this AppNap is working.
So first I want to define
a couple terms that we sort
of tend to throw around
a little bit loosely.
Power: so power is actually
a rate and it's the rate
at which energy is consumed.
One way to measure
that would be in watts.
Energy on the other hand is the
stored potential to do work.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And one way to measure that
might be in watt hours.
So for example let's say I
have a 50 watt hour battery
and I want a 7 hour battery
life from that battery.
Well I can do some simple
division: 50 watt hours divided
by 7 hours and that gives me
a rate of about 7.1 watts.
So what that means is that if
I'm using more than 7.1 watts,
my battery life will
be less than 7 hours.
And if I use less
than 7.1 watts,
I will get a longer battery
life of above 7 hours.
Now this 7.1 watts has to power
the entire system on a laptop.
That includes the screen and
the backlight, the GPU, network,
storage, memory and finally CPU.
Now you may look at
this list and think,
"There's some big
ticket items on there
like the screen and the GPU."
And you'd be right.
Those do use a significant
amount of power.
However today we're going
to focus instead on the CPU.
And that's for two reasons.
First, as software engineers,
this is the thing we're going
to have the most impact on.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But second and most importantly,
the CPU actually has the
highest dynamic range
out of all of these components.
So let me show you
what I mean by that.
If we look at the power
usage of a modern Intel chip,
you can see at its most
idle state we can use
as little as 0.4 watts.
However, if we're
executing any code at all
at the nominal frequency
of the chip,
we're going to go all
the way up to 15 watts.
Now this is just for the CPU.
So here you can see, if
we had a budget of 7 watts
and we're running a 15
watt CPU, we're not going
to get the battery life
of 7 hours that we wanted.
And furthermore, if we're
running at turbo frequencies,
then it goes all the
way up to 25 watts.
So there's a gigantic difference
between the low power idle state
and the high power executing
code or turbo states.
And so that leads us
into the three key rules
of extending battery life.
The first is that
we need to stay
at that low power 0.4 watt
idle state as long as possible.
And to do that - Number 2 - we
need to avoid unnecessary work
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
like drawing a pair of
eyes in the background
when you can't even see them.
And third, when we do work
which is okay from time to time
if the user's asked us to
do it, we need to return
to that idle state as quickly as
possible so that we can remain
in that 0.4 watt idle state.
Now you might be surprised
at how quickly we can actually
switch between these states.
So here I've got a use case
of visiting the www.Apple.com
website in Safari on Wi-Fi
on my demo machine, and
what I've graphed here
on the vertical axis
is CPU activity.
This is not the same
as CPU time.
For one thing, it's the whole
CPU: not just one process.
And also, it's more
a measurement
of how long we could stay
in that idle state instead
of how much time
we were executing.
So at the bottom
at zero percent,
that's our most idle state
for the entire sample period:
that's the lowest power.
And at the top, 100 percent
would be the least idle,
executing all the time
during the sample interval.
So here's what it looked like.
At the beginning, I was typing
Apple.com into the address bar
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and so that's not just the key
presses that we need to handle:
that also includes Auto Complete
or looking up bookmarks,
drawing the actual menu
on screen and so forth.
This vertical spike here
is where I've hit Enter
and we began downloading
the webpage
and rendering the content.
Now what's interesting
about this, is if you look
at the number there, we're
still at this time at the peak
of the activity of
this use case.
Over 50 percent of
the time is spent
at our most idle
state in the CPU.
So that indicates just how
quickly we can transition.
And finally near the end here is
when the data has finished
coming in - asynchronously
of course - and the
rendering has been finished.
And we've tried to move as
close to as much as idle
as possible when that's done.
Now let's compare this with
our Eyes Demo Application
that we just saw.
So you can see here, the Eyes
Application only lets the CPU
stay in its most idle state
about 75 percent of the time.
So that application - while
hidden behind another window -
is using significantly
more energy than Safari,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
rendering an entire webpage.
So even small applications
can have a large impact
on battery life and energy.
And you know, this is what
it was doing: polling.
The whole time, just polling,
checking where the mouse was,
redrawing where the eyes
needed to be and so forth.
And we can actually
do better than this.
So why would we exit the idle
state when there's work to do?
Well so for Safari, when
it was receiving data,
that's network activity.
That's a good reason to
wake up and do something.
Or when I was typing
Apple.com into the address bar,
that's another reason that
we might be doing some work.
We can actually transition
pretty fast between these states
such that disk I/O is a good
opportunity to save some energy.
And finally and most
importantly,
we exit idle because of timers.
And when we were
investigating this feature,
we found out that timers were
by far the Number 1 reason
to exit the idle state.
And that's because
there's so much API
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that actually involves timers.
That's everything
with a relative
or an absolute deadline.
So for example, NSTimer,
CF run loop timer,
dispatch source type timer.
These timers are both -- can
be both relative or absolute.
An API like sleep: let's
say we sleep for 1 second.
Well that means in 1 second, the
CPU has to exit the idle state,
wake up and continue
executing code.
It also includes
APIs with time outs
like pthread-cond-timedwait
and so forth.
And APIs built on top of
these like Perform Selector
with object after delay or
NS Run Loop run until date.
That last one?
Sometimes you see a
pattern of putting
that with a short timeout
inside of a wild loop.
So that would indicate
a lot of timers.
And many more.
So when developing AppNap,
we realized that in order
to effectively extend the
battery life of our laptops,
we needed to reduce
the impact of timers.
And we've done that
with two approaches.
The first is timer coalescing
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and the second is
timer rate limiting.
Let's talk about
coalescing first.
So here I have another
graph for you.
On the vertical axis,
I'm graphing power.
So the idle state of the CPU.
And then on the horizontal
axis, we've got time.
And here you see the time
scale's pretty short:
about 150 milliseconds.
Now I've set up 4
timers and T1 through T4.
The width of that line
represents the time I spent
doing work.
Because of that timer fired,
we needed to do something.
Now I said earlier that
we could transition
between that 0.4 watt idle state
and the high power executing
code state very rapidly.
And that's true but
it's not instantaneous.
There's actually a ramp
up and a ramp down time
between the idle state
and the high power state.
And while we're doing
that ramp up or ramp down,
we're doing absolutely zero
useful work for the user.
So that's overhead.
And in fact, because there's a
time, you can see from the slope
of the line between T1 and
T2, there's not enough time
between that deadline to go
to our lowest power state.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So instead, we choose
something a little bit less.
And the same thing
happens between T2 and T3.
However, if we look at
the T3 and T4 timers,
you can see they're right
up next to each other.
And that's something that
we can take advantage of.
So what we've done with timer
coalescing is apply a small
delay to timers to sort
of push them together.
And the result is that we can
ramp up once and ramp down once.
And according to our rules
of extending battery life,
we can stay in the
idle state much longer.
And if we compare it to
our previous graph here,
you can see that these areas
represent saved energy.
That was work that we just
didn't have to do transitioning
between states because
again remember,
we're not doing any
useful work there.
Now I said that the width
of that line represented
the work that the timer did.
And we call that a
Second Order Effect.
So what happens after
the timer fires?
So that you might do -
this one seems obvious -
you might use the CPU but you
could also turn on the screen
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or do some drawing
like the Eyes App does.
That involves the GPU
which means we have
to power that up as well.
Sending data over Wi-Fi means we
have to power up Wi-Fi radios.
Writing to disk or
writing to memory,
all of these have energy costs.
So in addition to
the coalescing,
we wanted to reduce the impact
of second order effects as well.
And to do that, we're
doing timer rate limiting.
So here is a similar graphic
except that I've zoomed way out.
So you see here that the
horizontal scale is on the order
of seconds at this time.
And what I have here is a
1 second repeating timer
and the width of each of these
bumps represents the second
order effect of the work
that's being done every time
that timer fires.
This is a very similar
pattern to what we see
in the Eyes Demo app except
there it's actually much
more frequent.
So timer rate limiting
pushes out the frequency --
excuse me -- reduces the
frequency of this timer firing.
And what that does is -- I mean
the timer's still going to fire,
but we're just going
to fire it less often.
And what that does is you
see here reduce the impact
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of the second order effect.
So all that area is
also saved energy.
And this can have a really
big impact over time.
A couple more details.
The coalescing delay
again is on the order
of about a hundred milliseconds.
And this is actually about
the same as delay due
to normal system load.
So in all previous
releases of OS X,
even if you thought
your timer fired exactly
when you asked it to, in fact if
there was a high priority thread
or some other work
on the run loop,
it could have been the case
that you actually fired later
than you did -- you
thought you did anyway.
Now we've designed this
based on heuristics
to be undetectable to the user.
The rate limiting delay
on the other hand is
on the order of seconds.
Now in both cases though,
we do not fire timers
early: we only delay them.
And the exact delays are going
to depend on the heuristics
so that we can provide a
consistent user experience.
And finally, some of these
behaviors are configurable.
And I'll show you how to
do that a little bit later.
But first I want to talk
about why: what's the result?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The end game in doing all of
this changing around timers?
So let's look again at this
graph for our Eyes Demo.
Again, this is CPU activity.
So here at about 25 percent
again is the Eyes app hidden
behind my activity monitor, but
with AppNap explicitly disabled.
And here is the same app,
hidden behind Activity
Monitor, with AppNap enabled.
And you can see that
these vertical bumps here
in the bottom line represent
the timer rate limiting.
That's where we allow the app
to continue to do some work.
Now looking at this
graph you might think
that that doesn't seem like
much of a difference, right?
We're both -- that's
only 25 percent.
That's not a huge number.
But if I were a user using on
my demo machine here the iZap
in the way I just described,
running it behind another
window is actually going
to cost me 1 hour
of battery life.
So the timer rate limiting
and timer coalescing
and other activities that AppNap
does can make a really big
difference for even the
smallest kinds of applications.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So one way to actually
improve this even further is
if in your applications
you think about switching
from timer based APIs
to event based APIs.
So instead of polling
key presses
or mouse locations, use events.
And instead of repeatedly
checking file content,
sometimes we see this as a form
of making sure an application
is updated or a form
of communicating
between applications.
So instead of doing that, you
use FS Events dispatch sources
which can tell you about files
changing or real IPC mechanisms
like NSXPC Connection.
Or if you're a - you
know - Cocoa level app,
you can use File
Coordination as well.
And finally, instead of
timer based synchronization,
use Semaphores or other locks.
So another pattern that
we've seen is an application
with 2 threads.
One thread does some work and
then sets a global Boolean flag
and the second thread
does a sweep
and checks the flag repeatedly.
And that's energy inefficient.
So we actually have an
entire talk about these kinds
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of patterns and ways
that you can do better.
That is tomorrow at 10:15 in
Marina, "Energy Best Practices."
But first, I want to show you
an example of the first thing.
So instead of polling mouse
locations using events.
So we're going to go ahead
and prove the Eyes
application to do this.
Alright, so here I
have the source code
that you've all been waiting
for, for the Eyes application.
Today though, we're
going to focus
on the application Delegate.
And let's start here
in the application did
finish launching method.
Now what you can see here is
that we are setting up a timer
with a timer interval
of 60 hertz.
And when that timer
fires, we are going
to call the Timer Fired Method
and of course it's
a repeating timer.
Now maybe I picked 60 hertz
because I wanted a smooth 60
frames per second animation
but anyway, that's a
pretty frequent timer.
And of course I add this timer
to my main run loop
so that it fires.
Now when it fires we call
this method Timer Fired
and you can see it
calls another method
which is Update Mouse Locations.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And here what we do is you
grab the current mouse location
from NSEvent and
we set up that --
we pass that information
along to our 2 views that are
in the application:
one for each eye.
So I'm going to go ahead and run
this again and we can see Eyes
over here in the corner.
We'll leave it there so we
can watch it watching us
through the demo.
And I want to show you a new
feature in Xcode as well.
So here is our new energy gauge.
And this is going to be
a really critical tool
for understanding the energy
impact of your application.
So you can see up
here in the CPU area,
we're in the single digit
percentage for CPU usage,
but the energy impact of this
application is actually very
high or high.
So this number up here
in the upper right corner
represents the number
of wakes in the last second.
So that means that's the number
of times the CPU had to move
from that lower power idle state
to the high power executing
code state just for this app.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In the middle we see a graph
representing energy impact.
The blue area is what I
was referring to earlier
as a second order effect.
So that's the work that
Eyes was actually doing
when the timer fired.
In this case, this app is
actually pretty inefficient
so that second order effect
is dwarfing everything else.
But that little red
area at the top
that you might see is what we
call the CPU Wake Overhead.
And that represents the overhead
of just having a timer at all.
So let's see how we can
improve the Eyes application
to reduce some of this impact
because as you can see here,
I'm not moving my mouse at all,
and yet we're still
using plenty of energy.
So I have a branch
ready to show you that.
So let's scroll down again
to our Application Did
Finish Launching method
and now you notice it's
much shorter and that's
because I deleted the timer.
In its place, I'm
using Event Monitor.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now this is an API that
was added all the way back
when we used cats for code
names and - Snow Leopard
in particular - and it's
called A Global Event Monitor
and A Local Event Monitor.
So we have one of each.
The Global Event
Monitor tells us
when an event happens
anywhere on the system
and the local one is
for events in our app.
So for this particular
app, I wanted to use both.
And you can see, I've
registered for notifications
about pretty much every kind
of mouse event there is.
But in any case, I
do the same thing
which is called the same
Update Mouse Locations Method
that we saw earlier.
And that's the only
change I've made.
Let's go ahead and
run this again.
And I'll bring up
my energy gauge.
And you'll see if I move
my mouse around some more -
and Eyes is following it here -
we still see our energy impact.
But look at the difference
up here in the right corner
with the wakes in
the last second.
So if the CPU is already awake
processing the mouse moved
event, that means a lot less
overhead transition time
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because now we're just doing
the work along with that.
And better still,
if I stop moving,
in general this app is going
to go to a steady state
of about zero wakes
in the last second.
You might see a few bumps or
blips in this graph and that's
because libraries or system
frameworks may be doing things
on your behalf that aren't
specifically in the application.
So those are normal.
But ultimately what we
want to see is zero wakes
in the last second,
zero CPU usage.
A steady state of zero is
the best state for energy.
But if I keep moving
my mouse again here,
you see that Eyes became
responsive right away.
So we didn't lose any
functionality in this app
but it's significantly
better for energy.
I want to show you
one more thing though.
I'm just going to go ahead
and drag my [inaudible] window
over here to hide the
Eyes app and I'm going
to move my mouse around.
Now what you can see here is
that we still actually
have an energy impact.
And that's because Eyes is
still receiving those events
in the background, even
though we can't see it.
So this is something that we
can actually improve with some
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of the new API that we've added.
So let's go back to our slides
and we'll talk about
how to do that.
So before I move on though,
I want to say a few words
about responsiveness.
I promised earlier at the talk
that AppNap is about both energy
or battery life and
responsiveness.
The idea behind this is
that important work should
have higher priority.
And we can use some of the same
heuristics that we've talked
about to decide which
work that is.
So what we can do is allow apps
or excuse me, put apps that are
in AppNap into a lower
priority for both I/O and NCPU.
So for example, let's say that
I'm editing my presentation
in keynote and an
AutoSave begins.
Well that's the foreground app:
the one I'm currently
interacting with.
I think we'd all agree that --
AutoSave's I/O should happen
first before any other kind
of like background work
like Logging to Disk
or some other kind of thing that
I can't even tell is going on.
So that's the idea
behind I/O prioritization.
For CPU, we're going to
lower the priority of apps
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in AppNap lower than other
apps but still higher
than system daemons or agents.
Really though, responsiveness is
about improving the general
performance of your application.
And that includes
reducing memory usage,
choosing better algorithms
and improving CPU efficiency.
And that's techniques that we've
been talking about for a while.
So we actually have an
entire talk about that.
It's called, "Building
Efficient OS X Apps."
It was yesterday so
if you missed it,
definitely catch
that one on video.
And remember that
improving performance
in that way also has a big
impact on energy efficiency.
So let's move on and talk
about some AppNap API.
So we have three new
categories of API.
The first is as I mentioned,
we can find out when you
application is visible
or a window.
We allow you now to add an
additional tolerance to timers.
That helps with the coalescing
that we've been talking about.
And finally, we also
let you tell the system
about what you're
doing as an application
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on behalf of the user.
And that will help us
improve our AppNap heuristics.
So first let's talk
about visibility.
This new API lets you find out
when either an individual window
or your entire application
is occluded.
So by occluded I mean
not visible to the user.
An example of when a window may
be occluded would be if it's
on another space and that
space is not visible,
if another application has a
window in front of your window
and your window is a hundred
percent occluded behind it,
or if some higher level
window just covers everybody
like the screensaver or if
you fast user switched away
from the user session.
So one example of when you
might want to use this is
to halt expensive
work when occluded.
So for example, the Eyes
application, it does not need
to listen for mouse events
if you can't see the eyes.
So that would be a big
improvement to make for that.
You can also use it to refresh
content when becoming visible.
So let's imagine we have a
stock ticker application.
Now stock data is pretty rapidly
out of date so it's a real waste
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if the user can't even see the
window if we not only redraw it
on the screen but also
spend power using the Wi-Fi
to retrieve that data, the
user's bandwidth especially
if they're mobile, and
your server bandwidth
to retrieve the data
in the first place.
So using this API can be a
big win for the entire stack.
So here's how that works.
Let's say I have my Eyes window.
Here it's visible right in
front of another window.
So partial visibility
like you see here,
we still count that
as visibility.
And in fact, it doesn't
matter how much
of your window is showing.
So maybe you would say,
"But I just have a
sliver of my app showing."
Well, we still consider
that to be visible.
And we also don't care which
part of the window is showing.
So maybe it's just a title bar.
That's still visible.
But if I finally move that
guy behind another window,
then we consider that
window to be occluded.
As far as minimized windows
go, if you minimize the window
to the dock, we consider
that to be occluded.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And for application
occlusion, it's the union
of all application windows.
So what I mean by that
is if even a little bit
of any window is showing,
then your application
is considered visible.
So all windows have
to be occluded
for your application
to be occluded.
The exception is
the menu bar except
if you have a status
item like this.
I don't know why Eyes would
have status item but if it did,
it would certainly
be that MOG icon.
So if that status item is
showing, then that counts
as a window for your
application.
So if the menu bar hides,
then that window would
also be occluded.
Here's what the API looks like.
We have two forms.
On NSApplicationDelegate
there's application did change
occlusion state and there's also
an NSNotification if you prefer
to listen to it that way.
But either way, when you
receive that delegate message
or that notification, you can
query the current application
occlusion state by using the
NS Application Method Occlusion
State and it returns
a bit field.
So we only have 1 bit set right
-- or 1 bit defined right now.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
That's NS Application
Occlusion Stayed Visible.
So if your application is
visible, that bit is set.
If your application is
occluded or not visible
to the user, that
bit is not set.
The window API is almost
exactly the same except it's
on NSWindow.
So Window Did Change
Occlusion State
and NS Window Occlusion
State Visible.
So for example, in
the Eyes Application,
we can set up this method
to check the current
occlusion state.
And you notice I've
used a bit wise
and operator here not equality.
That's because it's a bit field.
So anyway, if the
application is visible,
then I can install
my event monitors.
And if it's occluded,
then I can remove them.
So let's move on to
talking about timers.
So we found when
developing this feature
that most timers actually do
not need to be hyper accurate.
And by that I mean
you know 1 millisecond
or less kind of accuracy.
So again, what we've done
with timer coalescing is apply
basically a default tolerance
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to all timers.
This new API allows you to
increase that default tolerance
and that allows the system to
fire your timer at the best time
in the window that you specify.
So for example, here
I have a timer.
It's set to have a start
date of 5 seconds from now,
a repeat interval of 7 seconds
and a tolerance of 3 seconds.
So what that means is this
timer can fire anytime between 5
and 8 seconds, 12 and 15
seconds, and so forth.
The system will decide
what the best time is.
So it maybe in the
middle of the window.
It could be right at the
beginning of the window
or it could be all the way
at the end of the window.
Now don't be surprised
if you see a lot
of timers firing near the
beginning of their windows
for now and that's
because we need more timers
on a system including your
applications to adopt this API.
And the more timers we have,
the better flexibility the
system has -- excuse me.
The more timers we
have with tolerance,
the better flexibility the
system has in aligning them
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to save the most energy.
Here's the API.
It's on NSTimer and
hopefully it should --
it's not surprising.
It's called Set Tolerance.
And there's a getter as well.
And we also have similar API
on CF Run Loop Timer as well.
So what I'll do here
is create my timer.
Again, it has a time
interval of 7 seconds
and it fires my Timer Fired
Method and it's set to repeat.
The fire date is
5 seconds from now
and it has a tolerance
of 3 seconds.
And then finally I
add it to my run loop.
We also have a similar
API on Dispatch Timers.
So if you haven't used
Dispatch Timers before,
they're a kind of
dispatch source.
This one is a dispatch
source type timer.
The queue there is
the queue upon
which my event handler will be
invoked when the timer fires.
And then I use the Dispatch
Source Set Timer API
to specify the start time, the
interval and the tolerance.
And finally, don't forget
to resume your timer
if you actually want it to fire.
This API has actually been
around since we introduced it.
The last parameter
before though was ignored.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So now starting in
Mavericks, we are going
to start obeying that value.
And that will contribute
to the overall efficiency
of the system.
I said earlier that the timer
rate limiting can be configured
and this is how.
So if you have 1 timer
that needs to be exempt
from rate limiting, then
you can set this flag:
Dispatch Timer Strict.
Now, I really recommend
caution when using this.
As we saw from the Eyes Demo,
the rate limiting
had a huge effect
on energy efficiency
of the system.
So we imagine that this is
only for the kinds of cases
where you have very strict
requirements like interaction
with hardware or perhaps some
kind of networking restriction.
So please be very
careful when using this.
In any case, if you use this,
you still always should
specify tolerance.
Here I've specified a
value of 700 milliseconds
or about 10 percent and that's
because even a small amount
of tolerance will
allow the system
to get much better
energy efficiency.
So please never pass zero
in for that argument.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So again we suggest
about 10 percent
of your interval as a tolerance.
Exact value is the -- going
to be application specific.
Maybe it's more or
it could be less.
And so we leave it up to you.
So this is an important note.
This tolerance is used
regardless of AppNap.
So that allows us to make timers
more efficient even in apps
that are foreground or
are actively drawing.
So we can get even
better energy efficiency
by adopting tolerance.
Again, the strict
timer should be rare.
It disables the timer
rate limiting.
But even if you do that,
you should still
specify a tolerance.
And finally as I mentioned,
we'd really like all
timers to have tolerances.
And that's because
the more timers
that have a window
associated with them,
the better we can align
them across the system.
Next let's talk about
user activities.
So this is our new API
to improve the accuracy
of AppNap heuristics.
It's used for long running
or asynchronous work
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and it's a Cocoa -- it's
a new Cocoa level API
to prevent idle system sleep.
So if you've used the I/O Kit
Power Searching API in the past,
now we have a Cocoa API
for you to use as well.
And it also has a new
interface to automatic
and sudden termination.
So here's he API.
It comes in two forms.
The first is called
Perform Activity
with Options Reason Block.
The first argument
describes the kind of activity
that you're going to do.
And we'll see what
those are in a minute.
The second argument
is a reason string.
And I strongly encourage you
to always specify a
useful reason string here.
And that's because it
will show up in some
of our debugging tools.
And I'll show you that
in a minute as well.
For this form of the API,
the block is performed
synchronously.
So that means you
don't have to worry
about ending the activity
when you're finished.
You can just do it
all in one chunk.
And the system takes
care of the rest.
If however you want to do it
asynchronously, that's fine.
That's the second
form of the API.
It's called The End
Activity with Options Reason.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Those two parameters
are the same as before
but this one returns an object.
And you can either store that
object in an IVAR or put it
in a collection or
whatever you want.
The important part is to
remember that when you're done,
call end activity and
passing that object again.
So here are the kind of
activities that you can specify:
if you're doing something
that's explicitly user initiated
like exporting a file or
recording or processing.
For example, anything the user
might choose from the menu item
and then you know actually pick
or click on a button and do.
You use the NS Activity
User Initiated Constant.
Now when developing
this feature,
we found that many
applications hadn't
yet considered what the behavior
of their application should be
with respect to the system
falling into idle sleep.
So maybe my export
takes 30 minutes
but the user has asked the
system to idle sleep after 15.
So is that something
that the user expected
to finish beforehand and then
let the system sleep or not?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And that's sort of really up
to you in your application
and your specific use case.
So when you're using this
API, we really want you to sit
down and think about it.
Which of those behaviors
should it be?
So the NS Activity User
Initiated Constant actually
includes a no idle
system sleep assertion.
So that means while
your activity
with this option is active,
the system will not be
allowed to idle system sleep.
And we think that's the
right choice for activities
that are explicitly
done by the user.
If however you think
that it should be allowed
to idle system sleep, then
the next constant is the one
for you.
If you're doing some
kind of maintenance
or other background work,
you can use the NS
Activity Background Option.
And finally, if you're doing
something that's latency
sensitive like strict timers,
we think this is pretty rare,
you can bit wise or NS Activity
Latency Critical with any
of the previous flags to get --
to indicate that
kind of requirement.
So I said earlier, this
is a new API to interface
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with I/O K Power Assertions and
this is how you would do it.
You can begin an
activity with the option
of NS Activity Idle
System Sleep Disable.
And while that activity
is active,
again the system will not be
allowed to idle system sleep.
And we have a constant for
displaced sleep as well.
And finally, you can also use
this for sudden termination
and automatic termination.
So this is a little
bit different
than the existing count API on
NSProcessInfo and Foundation.
So you can use either
one that you like.
The idea behind this one is
that with the synchronous method
you don't have to remember
to decrement your
counter afterwards
so call in the End Method.
And with the object based API,
it might be a little bit easier
to track the assertion
that you're holding
so you can keep it in IVAR
like I said or if you lose it,
you can track it with
leaks and instruments.
Here's an example of
how you might use this.
I'm going to have an
NSOperationQueue
that has long running
asynchronous user work
and I will begin when the user
has chosen my you know Record
Option from the File menu.
Then I'm going to begin -- or
excuse me, Batch Process Option.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Then I'm going to begin
activity with options.
Here it seems like something
that should keep the system
from falling into
idle system sleep
so I chose NS Activity
User Initiated.
And the reason is because
I'm batch processing files.
Then I add my work to the queue.
And when that work is done, then
I remember to call End Activity
with the token that I got back
from the previous API call.
So you can actually have many
of these activities
at the same time.
The system will do the right
thing with all of them.
So for example, let's
say that you have a piece
of your application that's doing
some kind of background work.
It can use the NS Activity
Background Constant.
But then simultaneously the
user chooses a menu item
from the File menu.
Then that could be an NS
Activity User Initiated activity
and will do the right thing.
You should avoid
rapidly starting
and ending these activities.
Again, they're for long running
work or asynchronous work.
We automatically handle this
for you for event call outs
on the main thread for example.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And finally, please remember
the idle system sleep assertions
should really be used with care.
Don't prevent idle
system sleep forever.
So we've been talking
about efficiencies gained
by moving timers by a little bit
but nothing's worse for the user
than leaving their laptop
doing some work at night
and they come back in
the morning and it's
at zero percent battery because
some application just kept the
system from sleeping
appropriately.
So please verify
in your application
that the power assertions
are dropped
when you're doing that work.
Here's how you can do it.
Use the PM set, Command
Line tool.
[Inaudible] -g assertions and
it gives you a great output
that tells you which
applications
on the system have taken
which kinds of assertions.
So here I've modified Eyes
inappropriately of course
to keep the user's
computer awake.
So we can see there,
it's been doing it
for 3 minutes and 36 seconds.
And the reason string is the one
that we passed into
our API earlier.
So this is a great example
of why passing a real
reason string can be useful
to help you debug.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So use this tool to verify
that you have an assertion
when you expect and
that when you're done,
it's actually not
listed here at all.
So let's go back to our
demo and show you --
I want to show you how we can
improve the Eyes application
with some of the API
that we talked about.
So when we left off,
I was running the Eyes
and I hid it behind X Code
but it was still using
energy even though it was
completely occluded.
So we can use the new
occlusion API to improve that.
So I have a branch
ready for that as well.
[ Pause ]
And what I did is added our
Application Did Change Occlusion
State Method.
And you can see here, that
when that's called I query the
occlusion state again
using the bit wise and,
and if we're visible I install
the event monitors and if not,
I remove the event monitors.
It's pretty straightforward.
So I added this Remove Event
Monitors Method as well
and this uses the NS Event API
that we talked about earlier
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to remove the event monitors
that we had installed.
So let's go ahead and run
our application again.
And we'll hide it and
bring up our energy gauge.
And again, there might be
some activity that you see
on the graph here
as system frameworks
and libraries do a little
bit of work on your behalf.
But in general, when
I move my mouse,
we stay at about zero
wakes and zero impact.
So this is going to
be a much better story
for energy efficiency than
even the last version of Eyes.
[ Pause ]
Here we go.
Zero wakes and zero activity.
Alright, I want to show
you one more thing.
I've added a new feature to Eyes
which I think you'll
really like.
Let's go ahead and run it.
Now if I go up to the File
menu and choose Record,
you can see here in the
corner that Eyes is telling us
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that it's currently
recording our mouse movements.
And if I move my mouse
around here for a while,
and I stop recording, it
tells me I travelled 7
thousand points.
That's a pretty good score
but I've actually done
better in the past.
So anyway, let's go back
to our source code here
and see how that works.
I added a new method
called Toggle Recording.
And recording since I choose
it from the File menu seems
like a user initiated action.
So what I do is if I'm
not already recording,
I tell the system that by saying
Begin Activity with Options,
NS Activity User Initiated
Allowing Idle System Sleep.
And I give it a reason as well.
Now I said earlier that
when you use this API,
I want you to think
about what kind
of options you should choose.
So in this case I said, "Well,
if the user's not
moving their mouse,
well then there's really
nothing for Eyes to do.
So there's no reason for Eyes
to keep the system from falling
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
into idle system sleep."
And so that's why I chose the
Allowing Idle System Sleep
version of the options here.
And I want you again to think
about that when you look
through your use cases.
Finally I set up some
iBars and the UI and then
when the user chooses that menu
item again, we end the activity
by calling NS Processing.
So it's End Activity with
the User Activity Token.
So we make sure that we
balance our begin with an end.
The only other change I really
made here was you know a set
up method, I need to look
at both the occlusion state
and our recording state so
I know when it's appropriate
to have the event that
monitors installed.
So the result is that if I bring
up Activity Monitor again here
and begin recording with
Eyes, and we'll go ahead
and occlude it, we'll notice
that this time the AppNap column
- I'll zoom in for you here -
the AppNap column remains at No.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And that's because using the
NS Activity Options API we've
helped tell the system what
the user is actually interested
in doing.
In this case it was
recording the mouse location.
And so we can factor
that into our heuristics
about whether it's appropriate
to put the app into
AppNap or not.
Now if I go back to Eyes
here and stop recording
and occlude it once more,
we'll see that because we told
the system that we're done
with that activity, that
allows it to incorporate
that into our heuristics and
we'll that it now is allowed
to go back into AppNap.
Let's go back to our slides.
[ Pause ]
So let's do a quick summary.
I want you to remember
what an impact software has
on energy efficiency.
It's easy to think that
it's just a responsibility
of you know this backlight
or the screen or the GPU,
but the CPU is where
we do our work
and that is what drives
the rest of the system.
And the CPU has that
high dynamic range.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So what we're doing
in our applications
and in our system can
make a real big difference
on the user's battery life.
Remember the three key rules
of extending battery life.
First, we need to stay
at idle, that low power,
0.4 watt idle state
as long as possible.
Second, we need to
avoid unnecessary work
like redrawing a pair
of eyes behind a window
when you can't even see them.
And third, when we're
doing work,
we need to race back to idle.
That is return to that idle
state as quickly as possible
because the longer we spend
in our 0.4 watt idle state,
the longer the battery
life is going to be.
And also please remember
that avoiding timers
allows for longer idle time.
If you can, instead use event
based API like the Event Monitor
that we used for the
Eyes application.
And finally, if you must use
timers, please add tolerance.
And actually also,
use the Activity API
to inform the system
of important user work
like recording, batch
processing, and so forth.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And please remember
to take into account
that idle system sleep
assertion and verify
that you are taking them
and dropping them correctly.
We've had a bunch of
related sessions this week.
Most of them have already
happened so if you missed them,
these are great sessions
to catch on video:
especially the first one where
we had a great talk about -
in depth - the new features
of some of our new --
the new Intel processors and how
that has an impact
on battery life.
Building efficient OS
X apps: talked a lot
about improving the
responsiveness
of your application.
The next session which
was just this morning --
so a lot of the features
we've been talking
about today also apply
to web pages via Safari.
So if you're a web developer,
then please check out that video
to understand how
it impacts you.
And finally tomorrow
we have another session
on Energy Best Practices.
So that's going to talk a lot
about moving away from timers
and towards more event based API
for maximum energy efficiency.
If you're not here at the
conference, then please get
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in contact with our
Evangelism Team or talk to us
on the Developer Forums.
I browse that and try to
answer questions if I can along
with many other members
of the Cocoa team.
So with that, I want
to thank you for coming
and learning about AppNap.
[ Applause ]