WWDC2013 Session 712

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
>> Good morning.
My name is Matt Jacobson.
I'm on the OS X Performance Team
at Apple, and I want to talk
to you today about
Energy and Battery Life.
You've probably heard a lot
about battery life
this week at WWDC.
Battery life is really
important to us.
And hopefully by now
we've convinced you
that not only do you want
your apps to work great
and look great and for your
users to love how they work
and feel, but you want
them to be able to use them
and still get great
battery life.
And here's something
you probably don't want.
Users using their machine on
battery, as I see a lot of you
out there are right now.
If it's the middle of the week,
they see "Oh, the battery is low
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and I'll open up the battery
status menu and see what's going
on and there's some app that's
hogging all the energy."
Well, you really don't
want that to be your app.
So, I want to talk to you
about some simple ways
that you can make sure your
app is as energy efficient
as possible so you can avoid
being the energy hogger
on the system.
I want to talk about some new
tools and features in Mavericks
and Xcode 5 to help
recognize energy issues.
I want to talk about how
to diagnose those issues
once you've found them.
I want to talk about
a few common mistakes
that you can make that make
your app less energy efficient,
and how to avoid them.
And I want to talk about how
to adopt some best practices
and new APIs for
energy efficiency.
But first, let's start
with some background.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
What are the things that your
app can do to use energy?
Well there are mainly
three, running on the CPU,
doing I/O to the
network or a local disk,
or using the graphic system.
Now, the distribution
of how much power each
of these uses varies
from system to system.
But in all cases,
the CPU can dominate.
So let's talk about the CPU.
Now, you might think that with
our CPUs and our modern Macs
with really high clock
speeds and lots of cores
and multiple threads per core
that CPU time is
basically unlimited,
and in a lot of ways
that's true.
But CPU time is still
limited in one major way,
and that's by the
size of the battery.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And this is important
for two reasons.
One, the OS is designed
to give you basically
as much CPU time as you ask for.
And a single bad app, a single
app that is using too much CPU,
it is not energy efficient,
can totally ruin the user's
battery life and here's why.
The CPUs in our Macs, especially
the new Haswell ULT CPUs
in our new MacBook Airs
use a lot more power
when they're running
compared to when they're idle.
We're really good
about aggressively power
managing these CPUs.
But when they have to run code,
they run at a much
higher power state.
For example, if your app
is using 1 percent CPU,
the power draw from the CPU
increases by 10 percent.
That's just for 1 percent.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you're using 10 percent, the
power draw from the CPU doubles.
And if you have a thread that's
going full bore using 100
percent CPU time, the
CPU can draw 10 times
as much power compared
to idle or more.
So, how do you use the CPU
responsibly in your app?
Well, the main thing is this.
You should strive for your app
to be absolutely idle
when it's not in use.
So, that means eliminating
any work that's not driven
by the user.
So work done in response to
timers, work done in response
to chatting network,
work done in response
to inter-process
communication of any kind.
It means eliminating things
like persistent animations.
These things are
relatively cheap
but over the long term it
adds up to a lot of energy.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And then when the user
requests action, you want to be
as efficient as possible.
And the best way to
do this is to remember
that fast code is
usually efficient code.
So go back, look
at your algorithms,
make sure that they are
as efficient as possible.
If it makes sense to do
so, parallelize your work,
use Grand Central Dispatch,
harness the power of multicore,
and then race back to idle.
And the reason this
is important is
that the faster you guys
can get your apps idle,
the faster we can
get the system idle
and the less energy
we use overall.
If you want to know how much
CPU time your app is using,
just look at the energy tab
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the Activity Monitor
in Mavericks.
Just look at the
percent CPU column.
Again, you guys should
strive for this number
to be absolutely zero when
your app is not in use.
So 0.0. Even a small value
like 2.7 percent can cause a
significant power draw increase
from the CPU.
And so it's worth investigating.
If you're debugging in Xcode you
can go to the debug navigator
and look at the CPU gauge.
There's the debug
navigator, the CPU gauge,
and it will tell you how
much CPU time you're using
instantaneously as
well as a history
of how much you've
used over time.
And if you are the kind of
person who's more familiar
with the Unix shell, you can
run the top command of course,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
look at the percent CPU column.
Or if you pass -A, put
top into accumulator mode,
what this does is it allows
you to watch the CPU time
of your app accumulate
starting at 0,
so you can see here
I launched top
and Stock Watcher
has used 0.2 seconds
of CPU time since
I launched top.
So this allows you
to get an idea
of how much CPU time a
particular scenario takes
or just to watch your
app accumulate CPU time
in real time.
[ Pause ]
Now, if you see your
app using CPU,
you probably want to know why.
And this is where Instruments
Time Profiler comes in handy.
A lot of you are
probably familiar
with Instruments
having used it hopefully
to diagnose performance
problems.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you haven't used
Time Profiler,
what it does is it takes a
really high frequency sample
of your app.
So it's really quickly
seeing exactly
where it's executing all
the different call stacks,
and then it aggregates
all the call stacks
into a Call Tree view that
will show you what percent
of the time is spent where,
under which functions,
which libraries, which
frameworks, et cetera.
I'll show you a little
more on how
to use Instruments
a little later.
One thing that's
new in Mavericks is
that the system will also
automatically monitor your
CPU usage.
So you may see a line in the
console that looks like this.
You've got some app
caught burning CPU.
It's used a lot of CPU
over this much time.
And when this happens,
the system will automatically
drop a log
into
/Library/Logs/DiagnosticReports
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with the CPU resource.spin
extension.
And what this log contains is a
historical sample of your app,
where it's been using CPU time.
And this is automatically
done for you
without having to
set up anything.
And so this is basically
the same thing
that Instrument is
getting, just in text form.
Now, it's important to
remember that the threshold
for triggering these
is relatively high.
Here it says 50 percent CPU.
So, you should really treat
these as a real red flag
when you see them,
if you see them,
because they can cause a
significant battery life
decrease for your users.
So go through and make sure that
these reports represent work
that you know the user
is aware of and ask for.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Another way to diagnose
CPU time is to look
for what I call secondary
indicators.
So what do I mean by this?
Well, we talked about
the three ways
that apps can use
the-- or use energy.
We talked about CPU.
But of course using
the I/O system,
using the graphic system
also involve using the CPU
to do that.
So looking at where
you use the I/O system
or the graphic system can
give you a good pointer as to
where you're using CPU time.
So let's look at I/O.
This little tool called
fs usage that has shipped
on OS X forever, and what it
does is it tells you all the
places where your app is
interacting with the file system
and network layers
of the Kernel.
So these are things like, you
know, open/closed system calls,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
read/writes from the network,
disk I/Os, things like that.
So let's run it on an app.
Here you can see my app is
making a lot of calls to stat
for a file called myicon.png.
It's also actually writing to
a file called downloads.plist.
So, there's no magic
bullet here,
but this can often give
you a pretty good sense
of where you're using
CPU, at least what kind
of processing you're doing.
If you're interested in fs
usage or performance in general,
there was a great
session earlier this week,
Building Efficient OS X
Apps that talks a lot more
about fs usage, talks a lot more
about being a good
system citizen
with respect to system
resources.
So I highly recommend you
go back, give that a watch
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
when you get a chance.
OK, so that was I/O.
Let's talk about graphics.
So for graphics we have a
little app called QuartzDebug,
and this ships in our
developer toolkit.
And if you launch this app
and check the little
Flash screen updates box,
what this will do is it will
cause all draws to the screen
to be flashed in yellow.
So I check this box, put
up the OS X shutdown panel,
and the Shutdown button
was covered in yellow.
And this makes sense because
the button is pulsing.
I expect it to be drawing.
But if you have a UI
that looks pretty static,
you might be surprised to
see that, in some cases,
it's actually drawing.
And of course drawing
uses energy just
because it's using the
graphic system, it uses energy
because it's using the CPU.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And in a lot of cases, this
drawing could be invisible
or almost invisible
to the naked eye.
So what QuartzDebug
does is allows you
to see it a lot more clearly.
QuartzDebug is available in our
Graphics Tools for Xcode package
on developer.apple.com.
So, CPU usage.
Remember that a little bit of
CPU usage, even like 1 percent,
causes a significant power
draw increase from the CPU.
So, it's important to make sure
your app is absolutely idle
when it's not in use.
Go through, watch it
with Activity Monitor,
make sure it's idle
when it should be.
All right, let's
talk about timers.
I'm sure you use timers in
your app in a lot of places.
Timers are used to schedule
periodic events or future events
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and drive animations,
you use timers.
So why are timers interesting
from an energy perspective?
Well, timers keep
your app out of idle.
When you have a timer and it
wakes up and it runs some code,
that's keeping your
app out of idle.
That's bringing the
system out of idle.
And perhaps more importantly,
timers can have an
outsize effect on energy.
So what do I mean by this?
Imagine you have an app, and
it's got some repeating timer,
here are all the places
where it's firing.
Now, every time one
of these timers fires,
there's a little bit of overhead
involved in bringing the CPU
out of idle, bringing
your app out of idle,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
getting your app
context switched to,
getting your app
back to userland.
So, there's a little extra wake
up cost associated with that.
But it gets worse
because we need more
than just the CPU
to run your timer.
We need all the supporting
chipset.
So we need the memory
controller,
we need the bus controller
and all that.
And we really try
and aggressively power
manage these chips.
But they take a little
longer to go back into idle
and to come out of idle.
So if you have really
frequent timers like this,
the system might decide,
"Well, I don't really have time
to put those chips
back into idle.
I'm just going to
leave them on."
So you incur all of this
energy overhead, all this red,
even if this timer is just
doing a little bit of work.
By the way, what do
I mean by timers?
Of course we have explicit timer
APIs in Grand Central Dispatch,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Core Foundation -- foundation.
There are low level APIs
like sleep and usleep
that are implemented
with timers,
also things like
pthread cond timedwait
if you're using pthreads
or dispatch semaphore wait,
if you're using libdispatch.
Even a lot of low level APIs
that take timeouts like select
and poll, even MockMessage
takes a timeout.
So these can all, if those
timeouts fire, cause timers.
And there are a lot
of high level APIs
that are built on timers.
So if you use dispatch after
or NSObject performSelector:
withObject:afterDelay.
Even things like an
NSProgressIndicator is
implemented with timers
to advance the little spinny
wheel or the barber pole.
Or if you use CVDisplayLink,
that is a timer as well.
In Mavericks, it's a lot
easier to see the timer usage
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of your app if you go back
to Activity Monitor and look
at the Idle Wakeups column.
This will show you the
number of times a timer fired
in your app in the last second.
So in this case, this app fired
10 timers in the last second.
There's also a new column
called Energy Impact,
and this is a measure
of the overall energy
impact of your app.
So that includes CPU
usage, timer wakeups
and all the overhead
associated with that,
and some other factors too.
So this is a good way
to get a quick view
of whether you're using
energy on the system.
If you're using zero,
that's great.
If you're using nonzero,
well, you want to know why.
You can get the same stuff
right in Xcode and Xcode 5.
Go back to the Debug
Navigator and look
at the Energy Impact Gauge.
This will give you the number
of wakes over the last second
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and a readout of your
energy utilization.
So once you see that you're
incurring a lot of wakeups,
you probably want to know why.
And there's a new tool in
Mavericks called Timer Fires
that will help you
figure this out.
If you just run it, give it the
process ID you're interested in,
and -s to enable stack traces.
What this will do is it
will log all the places
where your app has
woken up due to a timer.
So let's look at this.
Here it looks like my
app has a 2 hertz timer.
That's a CF timer
that's executing routine
in its app delegate
called timer fired.
And here it looks like we've
got a libdispatch timer,
a Grand Central Dispatch timer
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that is executing a routine
called updateWidgets.
And here's where the app
actually just call usleep.
So you can see the full stack
trace of where it called usleep.
All right, so let's
talk about some ways
that you don't want
to be using timers.
Here's a pretty naive example,
but it's one that we have seen.
Say you've got a thread that's
doing some long-running work
like waiting on the network.
And once it's done it's going
to set a global Boolean
to say "I'm done."
And then you've got a second
thread that wants to know
when that work is done and
then run a completion block
or a completion handler.
And the way it's going to do
that right now is it's going
to say, "OK, I'll
check 10 times a second
to see whether the work is
done," and a little loop there.
"And then once it's done, I'll
just run the completion block."
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Well, first of all, this doesn't
work for several reasons.
But the reason it's bad for
energy is that most of the time,
this timer is going to wake up.
It's going to check
whether the work is done,
and the work is not
going to be done.
And so it's going
to go back to sleep.
Come back up and say,
"Is the work done?
No. Is the work done?
No."
So, every time you wake
up, you're incurring all
of that red energy
we saw before.
So this is no good.
Instead, in this case, in
this kind of case you want
to use an explicit
synchronization method.
For most of you the best option
is Grand Central Dispatch,
so you can use a serial
dispatch queue or a semaphore.
If you're using pthreads
there are condition variables
that will do this for you.
So in this kind of case
what we'll do is I'll put
that long-running work
on to a serial queue,
and then I'll make sure I
put the completion block
on to the queue afterwards.
And then the completion block
will just automatically run
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
when the work is done, no
need to wake up and check.
Grand Central Dispatch
will just handle it for me.
Here's another example.
Here we've got a 2 hertz timer,
an NSTimer, that is trying
to find out whether
a file has changed.
So it's waking up saying
"Did this file change?
No. Did this file change?
No." And the common case is that
the file is not going to change.
The user is not changing
their files very much.
So this is really
inefficient obviously
because we're incurring all
that wakeup overhear every
time we have to wake up.
In this kind of case, you want
to consider using an
explicit event stream.
And there are tons of
them on OS X and iOS.
In this kind of case,
I'm interested
in the File System Event
so I can use something
like DISPATCH SOURCE TYPE VNODE
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for a small scale
particular file update.
If you're interested in multiple
directories you can use the
FSEvents framework
for file system-wide
or directory-wide events.
If you are interested in
events from other processes,
of course there's XPC.
Or if you want to use a
distributed notification,
there are distributed
notifications
at the foundation level and its
distributed notification center,
as well as BSD notifications.
If you're interested in device
notifications, there are a lot
of different ways to do that.
For example if you
are interested
in when a mass storage volume
is attached or removed,
there are disk arbitration
notifications.
I/O Kit publishes
notifications on when certain --
other types of devices
are attached.
And if you're interested
in events from the network,
of course there's Apple
Push Notifications,
there's also Bonjour,
and network reachability
notification, so you don't have
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to wake up and say,
"Can I reach the server?
No. Can I reach the server?
No." So in this case, I
will replace the timer
with a dispatch source.
I'll give it DISPATCH
SOURCE TYPE VNODE
and the file descriptor
I'm interested in.
And then when the event fires,
I'll just say, "OK, go ahead
and check for the file.
The system told me
that the file changed.
I'll go ahead and
run my handler."
OK, here's another common case.
This is a basic run loop type
construct where we're waiting
for an event, and we're doing
that by waiting on a semaphore
in this case, and then we're --
when we get an event we'll see
if we have work to do.
The problem here is
I've specified a timeout
to dispatch semaphore wait,
but I'm not really
doing anything special
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if dispatch semaphore
wait times out.
So this is a common mistake
because dispatch semaphore wait,
and a lot of APIs like it,
take a timeout parameter.
So a lot of people say, "Well,
I better specify
a timeout anyway."
Actually in most cases you
can specify a special argument
that says "No, just go
ahead and block forever.
Don't make this into
a timer at all.
Just block until I get an event,
or indefinitely otherwise."
And if you do specify a timeout,
make sure it represents a
meaningful state change to you,
something that you're
going to check
for like an error condition
or a timeout, you know,
like a network timeout.
Here's another case.
You probably know a
lot about memory leaks.
But you're going to
have energy leaks too.
So this is a case where I've set
up a recurring timer
to do some work.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And it's doing some
valuable work for me,
but then I don't
need it anymore,
but I don't invalidate it.
And so what's going to happen
is for the rest of the lifetime
of my app, I'm going to have
this timer that's waking up,
probably doing no work,
using lots of energy,
hurting the user's battery life.
This is no good.
So go through, make sure
that all the repeating timers
you have are invalidated
or cancelled correctly when
you no longer need them.
Finally, you really want
to specify timer tolerance.
You may have heard a little
bit about tolerance this week.
Tolerance is a hint
to the system
as to how much it can
batch work together.
And this is good because the
more we batch work together,
the less wakeup overhead
we have,
and the more the system is
idle in its low power state.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So it's really easy to specify
a tolerance for all your timers
if you have dispatch timers.
It's just the final parameter
to dispatch source set timer.
This is in nanoseconds.
If you have CFRunLoop timers,
there is new API in Mavericks,
CFRunLoopTimerSetTolerance, and
that takes a value in seconds.
And similarly for NSTimers.
OK, I want to show
you a quick demo now
on how this all works together.
So, I'm running Mavericks
here and Xcode 5,
and I've got this little
app called Stock Watcher
that I'm working on.
And it's just a little
stock ticker app.
It shows me the price and change
of all the stocks
I'm interested in.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And look, it's even
a good energy citizen
because it suspends
its animations
when it's not in the foreground.
But I'm really interested in
the energy efficiency of my app.
So let me go to the Debug
Navigator and I'll look
at the CPU gauge first.
OK, so my app appears to be
idle, but it says it's using 2
to 3 percent CPU time.
What I really want is for
my app to be absolutely idle
when it's not doing
any work for me.
So let me just click
Profile in Instruments.
This will take me
right into Instruments
and start running
a time profiler.
So, again, what Instruments is
doing is it's taking a really
high frequency sample of my
app, seeing exactly the stacks
where it's executing, and
then it will show me those
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the call to review.
So I'll stop it now.
And if I just option click
on this disclosure triangle,
I can see the full
call tree hierarchy
of where it's executing.
And in this case
it looks like, OK,
it's doing a lot
of drawing work.
OK. I can also open up
the Extended Detail View,
this will also show me the
heaviest stack during the
sample period.
OK, it's doing a lot of views,
view drawing, control drawing.
OK, so I know this has
something to do with drawing.
So let me try a different
method.
I'll close out Instruments.
If I run this again, I'll turn
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on show view drawing
here in Xcode.
And this is basically the
same thing as QuartzDebug
like we talked about before.
And what this does is
it cause all views,
right when they draw rect to
show yellow right beforehand.
And we can see almost
immediately,
it's this top S&P 500 view
that's blinking out of control.
OK, so that gives us
a pretty good idea.
Let me show you one more thing.
So I'll go into terminal
and I'll run fs usage
on Stock Watcher.
OK, so immediately we
see lots of output.
What's happening here it looks
like is my app is
calling a few system calls
on this stocks.plist file.
So list xsetter,
get xsetter, lstat.
OK, so I happen to
know what this is.
This is a beta version
of my app.
It's not getting data
from the network,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it's getting data
from a local plist.
So let me stop Stock
Watcher and go to there.
So I know this is in
a routine called check
for stock.plist update.
OK. I actually did some
debugging in here before,
and I noticed that even
if the file I'm interested
in didn't change, I'm still
updating the S&P 500 view.
So let me just move this
into my IF statement here.
And I'll build and run.
OK. So let's go back
into the CPU column.
Great. The CPU Gauge says
I'm using 0 percent CPU.
We can even turn on
Show View Drawing.
OK, awesome.
No View Drawing.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Great. But let's look at
the Energy Impact Gauge.
So my app is idle.
I really want my energy impact--
my energy utilization to be 0.
But right now it's low
and it also says my app is
waking up 10 times a second.
All right, so what I'll do is
let's run Timer Fires on it.
OK, so Timer Fires is telling
me that that same routine,
check for stock.plist
update is firing--
and it looks like it
ran 10 times a second.
So let's go back.
Check for stock.plist update.
OK. So this is actually
called on a timer up here.
So this is a timer--
and this is checking
to see whether the
plist file changes.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But it's firing 10 times
a second, and of course,
we're not changing
the plist file.
So this is a waste of energy.
So I'm going to rip this timer
out and I'm going to replace it
with a dispatch source.
[ Pause ]
OK. So what this is saying
is I'll get a file descriptor
for the plist.
I'll set up a Dispatch
Source type of VNODE,
say "Fire this on
my main queue."
And when it does fire,
I'll just run check
for stock.plist update.
Great. So let's build and run.
[ Pause ]
And if go back to the
Energy Impact Gauge,
you can see my energy
utilization is 0 most
of the time.
I still have a few other
things to debug, it looks like.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So-- but let me show
you one more thing.
So it's really important
to not just test your app
when it launches but also test
all the paths your app can go
down because your user is
going to hit those paths.
So I actually recently
noticed that, you know,
you saw that Stock Watcher
turns off its animation
when it's in the background.
If I switch away from Stock
Watcher during an animation,
the energy utilization
remains at high,
and Xcode is telling me
it has 90 wakes a second.
So this is really bad.
OK, I'll go back and do the
[inaudible] Timer Fires again.
OK, so it looks like the
timer that I'm interested
in is firing a routine called
SWT stock table view animator
animation step.
So let me go back in Xcode.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
OK, so this is the timer that
is actually moving all the stock
quotes when they animate,
and when it's over,
it's supposed to
end the animation.
Oh, OK. So what's
happening here is
if the animation is
still running then--
excuse me, if the animation is
not running, then we don't end
up invalidating that timer.
So let me just move this out.
[ Pause ]
I'll build and run.
All right, let's try again.
I'll switch away
right in the middle
of one of those animations.
Let's look at the
Energy Impact Gauge.
All right.
So, for the most part,
my energy impact is 0.
Great. This is exactly
what I want.
My app is absolutely idle
when it's not in use.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
OK, let's go back to the slides.
So timers.
Remember that timers involve
a lot of wakeup overhead.
So you want to make sure that
you don't have a high rate
of idle wakeups especially
when your app is idle,
not doing any useful works for
the user, not doing anything
that the user expects.
And if it is, use our new
tools and features in Xcode 5
and Mavericks to help debug it.
All right.
I want to talk about
one more things,
and that's background work.
So we talked a lot about
making sure your app is idle,
no CPU usage, no wakeups
when it's not in use.
But you probably have
some periodic work
that you know you have
to do every so often.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And I want to show
you how you can do it
in an energy-efficient way.
So, one example of this
is periodic animations.
We saw that Stock Watcher
is stopping its animations
when it goes into
the background.
Animations are-- can be
relatively inexpensive,
you know, optimized drawing
routines, low frame rate.
But remember that even a
small amount of CPU usage,
like an animation, can cause a
significant power draw increase.
So-- and in a lot of cases,
these animations might not
even be visible to the user.
So what Stock Watcher was doing
was disabling its animation
in the background.
It's just doing that
by implementing the
ApplicationDidResignActive,
and the
ApplicationDidBecomeActive
methods in its app delegate.
Or if you just need
to check at runtime,
you can ask NSApp is active.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But in some cases, you
might actually only want
to disable certain animations
when the window you're
looking at is occluded.
So something like that.
In that case, there's new API
in Mavericks to notify you
when an application or
window has become occluded.
If you're interested
in the application,
just implement the
applicationDid
ChangeOcclusionState
routine in your app delegate.
For Windows, just check
or implement rather
WindowDidChangeOcclusionState
in your window delegate.
If you want to see a
great example of this,
there's talk earlier this week
about App Nap called
Improving Power Efficiency
with App Nap, that
was yesterday.
Go back and watch
that in the WWDC app.
There's a great example
of how you can use these
to be a lot more
power-efficient.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Another way you can improve
the energy efficiency
of your background work
is to batch it together.
So if you have background
work that's really spread
out over time, this
decreases the amount of time
that we can get the
machine idle.
So batching it all together
improves the idleness
of the machine, reduces
wakeup costs, and allows us
to be overall more
energy-efficient.
So here's an example
of batching.
Say I have an image cache
in my app and I want
to make sure there
weren't stale images in it.
So every time I add
an image to it,
I also set a little timer
saying "OK, in 5 seconds,
run this routine to make sure
this image is not stale anymore,
or is not stale.
And if it is then remove it."
And then in that routine I
say, "OK, is the image stale?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If so, remove it.
If not, check again
in another 5 seconds."
Well, this is kind of bad
if I just have one image
in my cache then I'm going to
be waking up every 5 seconds,
making sure that the
image isn't stale.
If I have lots of images in my
cache, I'm going to be waking
up all the time at all sorts
of different time saying,
"Is this image stale?
Is this image stale?"
So this work can be
batched together.
So I could change this code to
say, "OK, when I cache an image,
just add it to the
list of images."
And then when I know I'm going
to have to do some other work
like handling a user
event, I can just go ahead
and kick off this cache checking
work to another dispatch queue.
That way, it doesn't interfere
with the user interaction.
Go through each of them.
Prune all the stale ones.
[ Pause ]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And you may have some periodic
work that is even lower priority
that can wait even longer.
And for that, we have a new API
in Mavericks called
Centralized Task Scheduling.
And what this does is it allows
you to tell the system, "Here,
I've got this bit of work
that is really low priority,
and it's actually-- it's a low
priority that I'm OK waiting
for the user to plug
their machine back
in before I start it."
So this is stuff like
cleaning up temporary files,
maybe sinking or indexing,
checking for new
updates is a common one.
So new API in XPC that
allows you to do it,
and I'll show you
how to use it now.
So first, you just create
a new empty XPC dictionary.
You say, "OK, this is a one-time
event," so you're telling XPC,
"This doesn't have to repeat.
It's just once."
I want this to happen in
an hour, 3,600 seconds,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but I'm OK if it doesn't start
for another 6 hours after that,
or even longer, maybe
even 24 hours.
So-- and then I tell
XPC that the priority
of this activity is maintenance.
And what this means is
"Hey, XPC, try to do this
when the user has plugged
back in their machine,
and also when they're not using
their machine so it doesn't get
in the way of responsiveness."
Then you just go ahead,
register your activity,
pass that dictionary in, give
it a label, and give it a block
with the work you want to do.
XPC will take care of the rest.
It'll run your block
on its own queue.
It'll, you know, you'll
have the work done for you
at an appropriate time,
and you won't interfere
with the user's battery life.
There's a lot more to XPC
activity, and XPC in general,
there's a session earlier this
week, Efficient Design with XPC.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you're interested, go ahead
and give that a watch online.
So if you only remember
one thing from today,
let it be this, that
a small amount
of CPU usage causes a large
power increase from the CPU.
So you want your apps
to achieve absolute idle
when they're not in use.
Now, I know you guys can
take this advice and go back
and make your apps as
energy-efficient as possible,
and I can't wait for that,
but I also want you guys
to integrate this into
your release process.
So test for energy efficiency
just like you would test
for performance or
reliability or correctness.
If you have any questions
about energy efficiency,
you can contact our Core
S Technologies Evangelist,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that's Paul Denbaum.
If you have questions
about Instruments,
you can contact Dave
DeLong or look
at the Instruments User
Guide on developer.apple.com.
And of course, you
can ask questions
on the Apple developer forums.
We've had a ton of
great sessions this week
about battery life and
energy, so definitely go back
if you're interested and watch
these, especially, you know,
Maximizing Battery Life on OS
X will give you a great sort
of overview of everything
we've done and everything we'd
like you to do for energy
efficiency and battery life.
If you're interested in App
Nap and most of you should be,
look at the Improving Power
Efficiency with App Nap session.
If you write for the web,
there's a great session
yesterday
about how you can make an
energy efficient website.
Of course there was
the XPC session,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and I highly recommend the
Building Efficient OS X session
which was on Tuesday
if you are interested
in just being a good system
citizen in general with respect
to energy performance
and everything.
Thank you so much for coming.
Enjoy the rest of the week.
Enjoy the bash.
Happy WWDC.
[Applause]