Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Good morning.
Welcome to Writing Energy
Efficient Code, Part 1.
I'm Anthony Chivetta from the
OS X Power & Performance team.
And hopefully, everyone
is feeling awake
and recharged this morning now
that you've had your first cup
of coffee, your iDevices
and Macs have a full
charge from overnight.
But unfortunately, as we all
know, as the day wears on,
sometimes our energy
can begin to drain,
we find our batteries
just aren't as full.
And I want to talk to you today
about how you can help make sure
your applications contribute
to extending users' battery
life for as long as possible,
and improving the
user experience
by making sure users
can continue
to use your app all day long.
And of course, you know running
out of battery life is
something we all hate.
So, hopefully, that in
itself is motivation to try
to help improve the energy
efficiency of your application.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to help improve the energy
efficiency of your application.
But if not, in OS X
Mavericks and iOS 8,
we've been helping
provide users better tools
to understand what
applications are contributing
to battery life on their system.
So, on the left in
OS X Mavericks,
we added to the Battery
menu a list of applications
that are using significant
amounts of energy.
And on the right, you can see
the new Battery Usage screen
in iOS 8 settings.
And with these tools, users
can make smart decisions
about what apps they use,
understanding how those apps
impact their battery life.
So, if your app is
energy inefficient,
you might find users
stop using your apps
or give them poor
App Store ratings.
And so, it's in everyone's
interest
to make sure your apps are as
energy efficient as possible.
So, with that, what are we
going to talk about today?
We'll start by going
through some general power
energy concepts.
So, you have kind of a
high level understanding
of what it is we're discussing.
And then, we'll go
through some specific ways
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And then, we'll go
through some specific ways
that you can improve
your energy use.
We'll start with the technique
of doing something never,
progress to doing
it at a better time,
discuss how to do
it more efficiently,
and finally, how to do less.
And then, for Part 2, in
the same room immediately
after this talk, Albert will
come up and talk specifically
about networking, location,
and sleep/wake issues
in much more depth.
And so with that,
let's get started.
So, first, what uses energy?
Well, the short answer
is everything
on the system uses energy.
Any resource you
might use as part
of your code's execution is
going to consume some energy.
And a few things you want to
keep particularly in mind,
the first is your
CPU consumption.
And CPU has a huge dynamic
range, using a little
versus using a lot
makes a big difference.
How you use Flash
storage in particular.
Flash also has a
big dynamic range.
So, any time you read or
write to a storage device,
you're going to incur
more energy consumption.
Networking, especially
on iOS devices
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Networking, especially
on iOS devices
with so many different types
of networking, can play,
can be a large factor in
your energy consumption.
And then Graphics, you might
do a little bit of drawing
in your app but this
can cause a lot of work
to happen downstream
the Graphics pipeline.
This is obviously not
an exhaustive list,
but just some areas of your
application you should kind
of keep in the back of your
head as we talk about some
of these power fundamentals.
So, let's look at
the graph here.
And we're going to see a number
of graphs like this today.
So, just to kind of orient
you, on the bottom here,
we have time going
across from left to right.
And on the vertical
axis, we have power.
So, you can imagine,
we took a device
and attached the power meter.
And we would like
to see a reading
that looks something like this.
There's a couple of features of
this graph I want to point out.
The first is this very low
idle power at the beginning.
Our devices are really
good at getting
into low-power states
when not being used.
And so, the idle power of
a device is very, very low.
We then have these portions
where the system is active.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We then have these portions
where the system is active.
Your code might have been
running, doing something,
were actively consuming
resources
and accomplishing work.
And then we also have
these intermediate states.
And these are places
where the system is idle,
but we haven't been able
to get all the way back
down to our lowest idle power.
We need some time to
achieve that state
and you can see at
the end, we do.
But if you have sporadic work,
we can stay in these
intermediate states
for a very long time.
So, ultimately, what we can
do is we can divide this graph
in half.
We can say everything in the
top half is the dynamic cost.
This is the cost associated
with actually accomplishing more
and more work, whereas the
bottom part is the fixed cost.
This is what we pay just
to have accomplished any
work whatsoever.
And this fixed-cost
concept can come
into play whenever you
have sporadic work.
So, you can imagine here,
we have a workload
that's doing a bunch
of little tasks sporadically.
Well, all of this blue area at
the bottom is the fixed cost.
That's a lot of energy
we're consuming
to get a very small
actual amount of work done.
So, any time you can help
avoid small sporadic work units
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, any time you can help
avoid small sporadic work units
by aggregating them together,
you can dramatically reduce
the fixed cost of your work.
And this is an easy way
to get energy savings.
And it's also important
to keep in mind
because your app
probably does lots
of things exponentially
concurrently.
And you have to think
holistically about the behavior
of your application to make
sure you're grouping work
together appropriately.
So, we've also mentioned
these terms,
energy and power, a few times.
Let's put some more
specific definitions to them.
So, first, power.
Power is an instantaneous
measurement.
So, as we're looking at
these graphs it's the value
at one point in time.
On the other hand, energy is
the area under that graph.
So, we might say that something
consumes a certain number
of watts as an instantaneous
power measurement.
But to accomplish a task,
we want to talk about joules
and the total energy it uses.
And it's important to keep
these concepts separate
because we can actually
trade power for energy.
So, let's imagine we have
a single-threaded workload.
The actual dynamic cost of
that work is just this top part
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The actual dynamic cost of
that work is just this top part
in blue.
And if we were to make
that single-threaded
workload multi-threaded,
we can have the same dynamic
cost but reduce our fixed cost
by getting the work done faster.
In this way, our
instantaneous power is increased
but our overall energy
consumption is decreased.
In essence, by getting
better performance,
we've also achieved
better energy use.
And that's a common
theme we'll talk about,
is that any time you
can improve performance,
it's also likely going
to improve energy,
and these things
go hand in hand.
So, that's our power
fundamentals.
Things to remember is
that work is a fixed cost.
For small workloads, that
fixed cost will often dominate.
For intensive workloads,
the dynamic cost will
usually dominate.
And better performance
often means better energy.
OK, so, let's dive into
techniques you can use
to improve the energy
consumption of your app.
The first we want to talk
about is Do It Never.
So, this might seem simple-if
you can avoid doing work,
avoid doing it.
That's certainly going to have
better energy consumption.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
That's certainly going to have
better energy consumption.
So, let's imagine we
have an application
and some ad that's animating,
maybe it's a scrolling marquee
like in our iTunes
or App Stores.
And this is fine, you know,
that's a nice user interface.
But what happens if another app
comes along and they're sitting
in front of your application?
Are you still doing the work
necessary for that, for your ad
or marquee or what it might be
to draw it up into the screen?
Are your timers still firing?
Are you still consuming energy
even though the user can't see
your app?
It's very important to make
sure that we're not doing work
to power user interface features
that the user isn't aware of.
So, on iOS, hopefully, most of
you are familiar with these.
There's two
UIApplicationDelegate methods
you can implement,
applicationDidResignActive
and applicationDidBecomeActive.
And you'll get didResignActive
when your app enters
the background
or becomes not visible on
screen due to, let's say,
the user gets a phone call.
And then of course,
you'll get didBecomeActive
when you are now visible again.
It's important to
use the pair of these
to pause any animations,
UI updating timers
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to pause any animations,
UI updating timers
that might be firing and make
sure your app gets as quiesced
and energy efficient
as possible,
because the user can't see any
of the work you're
doing to update the UI.
You can also listen to
the UIApplicationWill
ResignActiveNotification as well
in other parts of your code.
Now, in OS X, it's similar,
same application didResignActive
and becomeActive on your
NSApplication Delegate.
But OS X makes things a
little more complicated
because there are multiple
applications on the screen.
And so, on OS X, we
have something called
Occlusion Notifications.
So, this is a new feature
that was new in OS X Mavericks
and it lets you determine
the visibility
of a particular window
or application.
For application, you have the
delegate method application
DidChangeOcclusionState.
And for a window, you can check,
get
windowDidChangeOcclusionState
to know when a particular window
or the whole application
becomes visible
or becomes fully occluded.
And with the use of these and
the applicationDidBecomeActive
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And with the use of these and
the applicationDidBecomeActive
and resignActive,
which changes based
on what the frontmost
app on the system is.
So, you are active on OS X when
you're in the menu at the top
of the screen and
you resignActive
when another app
becomes frontmost.
So, between that and
occlusion notifications,
you can determine the full
state of your app on the system
and make smart decisions
about whether it's
appropriate to do work.
And some of you might want
to know how App Nap
factors into all this.
So, App Nap is the feature
reintroduced in OS X Mavericks
that can reduce an inactive
application's energy use
by constraining its resources.
But, the problem is that App
Nap relies on heuristics.
We have to make guesses
as to whether a user cares
about a particular application
at a particular time.
And so, there are cases where
we cannot put an app in App Nap,
because we're not certain
the app isn't in use.
But as a developer, you are the
authoritative source for this.
Once you've taken
into account things
like occlusion notifications,
you know whether a particular
piece of work is necessary.
And so, ultimately,
in a well-behaved app,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And so, ultimately,
in a well-behaved app,
App Nap should never
have an effect.
If the user isn't using the app,
you shouldn't be doing any work.
And if you are doing work
on behalf of the user,
you should be using
the NSProcessInfo
PerformActivityWithOptions
API to let the system know
that you're doing work and
now is not an appropriate time
to nap you.
So, ultimately, App
Nap is a fallback
and in a well-behaved
application you really shouldn't
have any effects from App Nap.
So, remember, try to
avoid unnecessary work.
Monitor the app, your
application's state to know
when it's not visible.
Avoid updating the UI until
the user can see the results
and make sure to be efficient in
napping yourself when not in use
so App Nap doesn't
have to take effect.
So, with that, let's talk about
doing it at a better time.
So, user devices have lots
of different power states.
Sometimes, they're plugged in.
Sometimes, they're on battery.
And when you do work, in effect,
what the user's overall
experience over a long period
of time is with respect
to their battery life.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, if we imagine here,
your typical user day, oh,
maybe not typical, but
the user forgets to plug
in their device overnight,
they get a little bit of charge
in the morning and then
they have this big window
until noon before they can
plug in their device again.
And our goal here is
obviously to make it
so the user never
runs out of battery.
So, let's take a
look at what happens.
We start out, user is doing OK.
They get a little
charge in the morning.
And now, they're
sitting around at 10 a.m.
and they run your application.
Well, your application
decides it wants
to do something very
power intensive,
wants to download new
content, do some update,
and that uses a lot of energy.
Well, now, we've dramatically
reduced the user's battery life
and they're going to run
out of energy before they
have a chance to plug in.
And the user is now very sad,
we're sad, everyone is sad,
this isn't a good experience.
But what if instead of
running that work immediately,
you knew this just needs
to happen sometimes soon.
It doesn't need to
happen right now.
And you were to tell the system,
please let me know
when a good time is.
Give the system a
window to schedule in
and then let the system
automatically move
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and then let the system
automatically move
that work out to a better time.
And now, rather than running out
of battery, our user makes it
to a time when they can plug in.
They're happy.
Your app isn't blamed for
causing their poor battery life,
and we have a general
improvement in user experience.
So, on OS X Yosemite, we have
a new foundation API called
NSBackgroundActivityScheduler
that you can use
to accomplish exactly this.
It allows you to
schedule an arbitrary task
for some good time
in the future.
It supports repeating or
non-repeating activities.
So, it's great for any periodic
updating you might need to do.
And you can use it
to schedule things
like periodic content
fetches, update installs,
garbage collection or
data maintenance tasks,
automatic saves or
backups-really anything you do
in the background that
doesn't need to happen
at a particular time,
you can sort of do
when the system conditions
are right.
So, let's take a look
at how to use this API.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The first thing you'll
do is create an
NSBackgroundActivityScheduler
object.
And you'll use the
initWithIdentifier method
to pass in an identifier for
that particular activity.
This is something that you
should put in reverse-DNS style
and use it to identify the
particular action you're
doing uniquely.
But you also want to try
to reuse these identifiers
over multiple invocations
of that activity or launches
of your app because the system
will use identifiers as a way
to learn about the
activity you're doing
and make better scheduling
decisions.
So, once you've created
a scheduler,
you can now specify
scheduling properties.
So, and let's say we want
to have something fire
in the next 10 minutes.
What we can do is specify the
tolerance to be 600 seconds.
And then, when we
run the activity,
we'll try to schedule it within
that 10-minute tolerance period.
On the other hand, if we
want to schedule something
out for the future, let's say
we want it to happen between 15
and 45 minutes from now,
we can specify an interval
of 30 minutes and a
tolerance of 15 minutes.
And what this means is
that we want this work
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And what this means is
that we want this work
to happen 30 minutes plus or
minus 15 minutes in the future.
And finally, if we want
the activity to repeat,
let's say we want to check
for new content every hour,
you can set repeats equals YES
and then interval to 60 minutes.
And now, we'll try to have the
activity run once each hour.
It's actually once each hour
in the sense that if you were
to break up time into one-hour
periods, we'll make sure
to run it once in each period.
So, your average time
will be once every hour.
But within a period, it
might happen sooner or later.
But the benefit of this is
that you won't experience
drift over time.
All right.
So, once you've specified
scheduling properties,
now it's time to actually go
ahead and schedule the work.
This is pretty simple.
You'll call the
scheduleWithBlock method
on the activity object.
And you'll pass in a block that
takes a completion handler.
In that block, you can do
whatever work you might need
to do and it's perfectly OK
to do that work asynchronously
and save off the
completion handler.
But then when you're done,
you'll call the completion
handler
with NSBackgroundActivity
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with NSBackgroundActivity
ResultFinished to
indicate that to the system
that now this activity is done,
you've completed the work.
Of course, if the work is really
long running, maybe, you know,
multiple minutes, it's possible
the system power state will
change during the
execution of that work.
In this case, you want to
give the system the ability
to tell you to pause and
later resume that work.
And you can do this by checking
the shouldDefer property
of the activity.
This returns YES.
The state of the system has
changed and we would like you
to defer the remaining
work until a better time.
So, you can check that property
and then call the
completion handler
with NSBackgroundActivity
ResultDeferred to indicate
to the system that it should,
that you're going
to pause the work
and that it should
call you back later.
We're using the same scheduling
parameters as you started with.
So, that's
NSBackgroundActivityScheduler.
You specify the scheduling
requirements for the work.
The system selects the best
time to perform that work.
We have support for
repeating tasks without drift,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We have support for
repeating tasks without drift,
and it's available in OS
X Yosemite, or if you want
to use a C API, it was available
as XPC activity in
10.9 Mavericks.
Now, this works really
well for CPU or I/O or kind
of other local intensive tasks.
But if you want to do large
transfers to or from a server
on the network, we actually
have an even better solution
and that comes in the
form of the NSURLSession
with the Background Session.
So, let's imagine we
have an application,
and your app has a number
of NSURLRequests it
would like to issue.
What the Background Session
lets you do is create an
NSURLSession, pass it
those NSURLRequests.
But then, instead of
creating in-process tasks,
those go out of process
to a system daemon
that can then handle
executing those tasks for you.
And if your app sticks around,
then you'll get delegate methods
called on your delegate just
like you normally would
with NSURLSession.
But the really cool thing is
if your app happens to go away,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But the really cool thing is
if your app happens to go away,
let's say the user quits it,
maybe the system reboots,
those tasks will stick around
in that out-of-process session
and get processed while
your app isn't running.
So, that means if you need to
do, you know, many hundreds
of megabyte download, your app
doesn't have to stay running
for that download to continue.
Then, when your app
gets relaunched,
you'll use the same
backgroundSession
ConfigurationWithIdentifier
call and make sure you pass
in the same identifier
as before,
and you'll get reconnected
with that existing session
and then get your delegate
methods called for those tasks
for whatever progress
has happened
on those tasks while
your app isn't running.
This is already great
features in an iOS.
It supports multitasking, so
your app can get re-awoken
to receive these
delegate methods.
But the really cool part
for power comes into play
when we talk about the
concepts of discretionary tasks.
So, there's a configuration
on the NSURLSession
configuration object.
You can set the discretionary
property to TRUE.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You can set the discretionary
property to TRUE.
And it's something
that's available in iOS 7
or now in OS X Yosemite.
And what this tells the system
to do is to pick the best time
to do the work based on
a variety of factors,
including system power state,
network state and more.
And it will automatically
provide things
like bandwidth monitoring
and automatic retry.
So, bandwidth monitoring
is important
because it's very energy
inefficient to do work
over super-slow connections.
And so, when you're using
the Background Session
with a discretionary task, we'll
monitor the effective bandwidth.
And if it falls below certain
levels, automatically stop
and later retry that task
to make sure we can download
it quickly and efficiently.
We can also, because of
this, do automatic retry.
So, if the network
gets disconnected,
we'll then automatically
retry the task later
when the network becomes
available, handling a variety
of edge cases in uploads and
downloads automatically for you.
Now, you can adjust this way
we schedule discretionary tasks
by changing the
timeoutIntervalForResource
property on the configuration
object.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
property on the configuration
object.
So, in this case, we
specified one day.
So then, we want this to happen
sometime within 24 hours.
Now, if this timeout elapses,
you'll get an error thrown.
So, generally, you want to
make sure this is long enough
that we can reasonably
do the download,
taking into account the fact
there might not always be
appropriate networking
available.
It's why anything less than
12 hours is probably going
to put stress in
the system's ability
to effectively do the work.
This was a very high level
overview to NSURLSession,
just enough to kind
of whet your appetite.
If you want to learn more,
check out yesterday's What's New
in Foundation Networking
talk, where they go
into much more detail
on the Background
Session and how to use it.
So that was Do It
at a Better Time.
We talked about how you can let
the system schedule your work
for power optimum execution.
But let's say you're
already executing,
maybe at the better time.
I want to talk now about ways
that you can do your
work more efficiently.
So, our system has a variety of
resource management properties.
Some of these affect the
responsiveness of the system
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Some of these affect the
responsiveness of the system
when a particular
task is going on.
This includes things like
the CPU Scheduler Priority
and the I/O Priority
for a particular task.
We also have properties that
affect the efficiency of work.
This includes the amount of
time we're coalescing or willing
to apply, or hence, as to
whether we should run the CPU
in a throughput or
efficiency-oriented mode.
Now, these properties
are very difficult
to specify individually.
It's complicated
to get it right.
And so, most developers are
simply left the whens they could
get by using these
properties on the table.
So, we want to make
this easy to be able
to use the correct values
for all these properties.
And in OS X Yosemite and iOS 8,
we're introducing
something called Quality
of Service Classes that
can help you do this.
So, there are four Quality
of Service Classes we have
defined on the system.
The first is User
Interactive, which indicates
that this work is involved
in creating a smooth,
buttery user UI.
It's these things like the
main thread, animations,
event processing, whether
that's touch events or meeting
or some other kind
of event processing
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or some other kind
of event processing
where we need very,
very short latencies.
User Initiated is
for doing request,
making servicing requests
that the user has made in ways
that we need to provide
immediate results.
So, these are clicks on an
object to get more information
about it in your user interface.
The task of getting the
details of that object
and populating them onscreen
would fall into User Initiated.
Utility, for longer
running tasks.
So User Initiated and User
Interactive are designed to be
as performant as possible.
Utility, we try to achieve a
good balance between throughput
and energy efficiency.
So, we want to put
longer-running tasks
where we don't want to
have an over, we don't want
to put a large power
drain on the system.
And then Background, for things
that are not visible
to the user.
So, with all these
classes, how do you pick?
So, the question you
want to ask yourself,
or User Interactive is, Is
this work actively involved
in updating the UI?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If this doesn't happen, will
the UI appear to be frozen?
This includes things
like the main thread,
which we handle automatically
for you, animations
or input event processing.
If the answer is no,
then you want to think
about User Initiated and
ask yourself the question,
Is this work required to
continue user interaction?
So, for example, is
this actively involved
in loading content
that the user needs
to see before they can make
the next user interaction
in your application?
If this isn't the
case, for example,
the user initiates a task
and it's long running
and displays a progress bar.
So, either they can
continue interacting
with your application
or you might expect them
to take a break or go switch
to another app on OS X.
You want to think about Utility.
In which case, the question
you can ask yourself is,
Is the user aware of the
progress of this work?
If it's a longer-running job
with a progress indicator,
that's perfectly
suited to Utility.
And then finally,
for Background,
that's the remaining work
that the user isn't
aware of the progress of.
And for Background work,
you want to ask yourself,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And for Background work,
you want to ask yourself,
Can this work be
deferred to a better time?
If so, use the
NSBackgroundActivityScheduler
object in addition to
running in Background.
So, let's say you've gone
through, you've thought
about a piece of work in your
application and you decide
that User Initiated is
probably the right Quality
of Service Class.
Well, the next thing you want
to do is ask yourself
a few more questions.
The first is, Is it OK if User
Interactive work happens before
my User Initiated work?
Is it OK for this
work to compete
with other User Initiated work?
And is it OK for my
work to take precedence
over Utility and
Background work?
Ultimately, these Quality of
Service Classes form a hierarchy
and the system will prefer
things higher up in this list.
So, you want to make sure
that within your application,
you've picked a set of
Quality of Service Classes
that let the system
appropriately prioritize
resources, which brings
us to what exactly happens
when you specify a
Quality of Service Class.
So, let's imagine we
have a Background Quality
of Service operation running,
and it's using a lot of CPU
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of Service operation running,
and it's using a lot of CPU
and has a lot of I/O going on.
And then, a User
Initiated task comes around.
Well, what the system is
going to do is it's going
to prioritize resources to
the User Initiated task,
letting that get the majority
of the throughput on the system
and letting that
work happen quickly.
Similarly, if we were to look at
power graphs for User Initiated
and Background work, in User
Initiated, we run the work
as quickly as possible,
but potentially
in power-inefficient ways.
Whereas, for Background work,
we will try to run the system
in power-efficient ways.
It might take slightly longer,
but your overall energy
consumption will be reduced.
And, of course, Utility falls
on the middle of the spectrum.
And so in this way,
by appropriately
classifying your Utility
and Background work, you can
both improve the responsiveness
of User Initiated and
User Interactive work
and improve your overall
energy efficiency.
All right.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
All right.
So, let's take a look at
an example application
and how we might apply
Quality of Service to it.
So, we have PhotoMeister 3000.
It's our kind of generic
photos application.
You connect the camera
containing a bunch of RAW images
and we pull off some
JPEG previews
and display a bunch
of thumbnails.
And then in the background,
load the full-size
images and convert them.
They, of course also
have search functionality
because every app needs
search functionality.
So, how do we apply
Quality of Service?
Well, User Interactive
is just going to be used
for the main thread
in our application.
And that happens automatically
without you having
to do any work.
User Initiated, we're going to
use for thumbnail generation.
And this is because the
user plugs in their camera,
and the next thing they want
to do is start browsing
the thumbnails.
And so, their ability to
make the next interaction
with their application is
dependent upon those thumbnails
being available.
So, it's going to run
at User Initiated.
Now, imagine that the user
is browsing these thumbnails
and they click on one to try
to view the photo full size.
Their next interaction, which
might be looking at the photos,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Their next interaction, which
might be looking at the photos,
scrolling through it,
depends on the ability
to load that full-size image.
So, we're going to load
just that particular image
in that case at User Initiated.
But all the other images we're
going to load off the camera
and convert should
happen at Utility.
The user might be able to
see the progress of it,
but you want this work to
happen in deference to the work
of updating the UI, scrolling,
displaying the thumbnails
and so on.
And finally, any
work we have to do
to update our search index
would happen at Background.
This isn't something the user
is aware of the progress of.
And so, we want it to happen
in deference to things
like loading the
images off the camera.
So, let's imagine that our
app, we're trying to figure
out how to build this thing.
Let's kind of create a simple
NSOperation-based approach
to writing this application.
We might create an
NSOperationQueue
for thumbnail generation
and create an NSOperation
for each thumbnail
we want to generate.
And we might create another
queue for image conversion
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And we might create another
queue for image conversion
and an operation for each
image we want to convert.
So, how does this play into QOS?
So, in OS X Yosemite
and iOS 8, NSOperation,
NSOperationQueue now have
a qualityOfService property
that you can use
to set what Quality
of Service particular
work should run at.
So, if we want to run an
NSOperation at Utility,
we can simply say
operation.qualityOfService
equals
NSQualityOfServiceUtility.
If you set a Quality of
Service on both an operation
and the queue, we'll use
the higher of the two.
And if you don't set
NSOperation, for example,
in the code you're shipping
today, we will attempt
to infer an NSOperation from the
environment whenever possible.
So, what this means is that
if you have code executing
at Utility Quality of Service
and you create a new
NSOperation inside of that code,
that new NSOperation will
automatically use Utility
if there isn't a Quality
of Service set later
on that operation
or on the queue.
So, if we go back to
our application example,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, if we go back to
our application example,
if we want to apply
Quality of Service to this,
the first thing we'll
do is set User Initiated
on our thumbnail generation
queue, and then Utility
on our image conversion queue.
And now, those pieces
of work will happen
at the appropriate
Quality of Service.
But Quality of Service
isn't static.
And the logical Quality
of Service
of an operation might
change over time.
So, for example, you start
doing a conversion of an image
at Utility but the user
wants to view the result.
Well now, that work needs to
happen at User Initiated Quality
of Service instead of Utility.
With NSOperation,
we have three ways
that we can promote the Quality
of Service of existing work.
The first is enqueueing a higher
Quality of Service operation
on the same queue as that work.
If you have a queue full
of utility operations
and you enqueue something
that's User Initiated,
we will then promote everything
in front of that operation.
Also, to User Initiated.
So, that operation gets to the
front of the queue and runs
at an appropriate time.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
at an appropriate time.
If you use addDependency and
make a, let's for example,
User Initiated operation
dependent
on a Background operation,
we'll promote
that Background operation
to User Initiated.
And finally, if you
use waitUntilFinished
or waitUntilAllOperations
AreFinished from a higher
Quality of Service thread,
we will promote the
operations you're waiting on.
So, if we go back
to our example,
and let's say you get an event
that indicates the user tapped
on a particular image
to view it full size,
the first thing you'll do is
find the operation associated
with converting that image.
You'll then adjust
this queuePriority
to set queuePriority very
high, and after that,
adjust its Quality of
Service to set its Quality
of Service to User Initiated.
And now, if this is our queue,
we've now promoted the operation
we care about to the front
of that queue by adjusting the
queuePriority, and then cause it
to run at User Initiated
by adjusting the
Quality of Service.
All right.
So, quiz time.
This is your turn.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We have another example
app and I'm going to walk
through the different features
of this app, and I want you
to think about what
Quality of Service each
of these features should run at.
Then, I'll let you
know the answer.
So, it's Feed Reader 9000.
It's a kind of typical RSS
or newsreader application.
So, if the user clicks on a
particular item in the feed
and we need to display
the content of that item,
the work to read the item on
our database, render the HTML,
generally get it onscreen.
What do you think
that might run at?
So, that's going to
run at User Initiated.
The user asked for it,
and so we need to display
that content before the
user can interact with it.
But it's not actively
involved scrolling
or otherwise be creating
a buttery user experience.
It's certainly longer
running work than that.
Now, imagine all the
users reading this item,
at the same time, we're
pre-fetching images
that happen later on
in this stream of news.
What Quality of Service
might that run at?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
What Quality of Service
might that run at?
So, we're going to want
to run that at Background.
The user is not aware of
the progress of this work.
And so, we don't need to
give it quite the performance
of Utility.
The Background work
will certainly happen
and we'll be able to
catch those images.
Of course, if the user then
goes to that post or wants
to promote that operation.
What about fetching
new content, new feeds?
So, this one is kind
of a trick question.
If the user requested
the content,
so they click Get New Items
or Get New Feeds button,
the expectation is that now
they're going to be watching
that list of items and be
unable to interact with it
until we've populated
it with new entries.
In that case, doing just the
minimum amount of work possible,
to get the list of items
that they can browse through,
should be User Initiated.
On the other hand, this
happens automatically.
Let's say we have a timer,
hopefully using
NSBackgroundActivityScheduler
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
hopefully using
NSBackgroundActivityScheduler
that causes us to fetch
new content every hour.
That work should
happen at Utility.
This is something the user
is aware of the progress of.
If it doesn't happen
then, obviously,
they won't see their new items.
But they're not watching for it
and it's not preventing
their further interaction.
And finally, search indexing.
This would happen at Background
because the user isn't aware
of the progress of this work.
So, you've taken
your application.
You've diligently gone
through and adopted Quality
of Service everywhere.
All your work is classified.
How do you actually validate
that this is working
and debug it?
So, there are three things
that you'll want to do.
The first is after
adopting Quality of Service,
set breakpoints to confirm
that your work is running
with the Quality of
Service you adopted.
Use the powermetrics
tool to confirm
for a longer running task which
Quality of Services are in use.
And then you can use the
spindump tool to determine
which Quality of Service
a particular piece
of code is executing with
if what you find using one
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of code is executing with
if what you find using one
of these other methods
is unexpected.
So, let's start with
the first of these.
In Xcode 6, if you first pause
your application, whether using,
just pausing it or setting a
breakpoint, and then you go
to the CPU debug gauge, which
is part of the debug navigator.
Most of you are probably
familiar with this.
The top, you see a graph
of CPU use over time.
And so, we can see
in our example,
we have something burning CPU.
If you then go down
through the threads list,
underneath each thread will
tell you what Quality of Service
that thread currently has.
So, in this case, this
thread has Utility.
And so, we know that that CPU
time is happening at Utility.
Now, this is the
Quality of Service
that your code requested.
So, if you start an operation at
a particular Quality of Service,
that's what will
be displayed here.
If another part of
your application tries
to change the Quality of Service
of that thread, for example,
using the override
API that you'll learn
about in a session later
today, that won't appear here.
So, let's say you've
gone through,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, let's say you've
gone through,
validated your initial adoption,
and now you want to know, OK,
what is my code actually
using in practice?
You can use the powermetrics
command line tool
with the show-process-QOS to see
where your code is
spending time.
So, in this case,
we'll run powermetrics
and we'll get a list
of running tasks
and we can see MyApplication
is here.
And I'm spending 80
milliseconds per second
on the CPU, or about 8 percent.
I can see some information
about timers, which we'll talk
about later in the talk.
But then, what's important to
us here is I get a breakdown
of what Quality of Service
Classes I was using.
So, I can see that I'm spending
88 milliseconds at Utility,
so almost all of
my time is there.
And if that's what I wanted,
then I've done a good job
adopting Quality of Service.
Now, if you find that what
you see here isn't expected,
you can use the spindump tool
to get a more detailed
understanding
of where your code is
executing with respect
to Quality of Service.
So, if you're not
familiar with spindump,
it's kind of like
sample or time profile.
It's a sampling-based profiler.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It's a sampling-based profiler.
But in OS X Yosemite, we added a
new option, the timeline option
that can show your stacks
chronologically instead
of by heaviest first.
So, in this case,
we've run spindump
against our application.
And you can see that we
have a particular thread
and it starts at
executing Utility.
And then, if later that thread
happens to change to executing
at Background, we'll see another
indication of our Quality
of Service of that thread.
In this way, you
can kind of walk
through what code
you're executed during,
what code your app executed
during the spindump,
and see what Quality of
Services that code was using.
So, that's Quality of Service.
It lets you specify
the responsiveness
and energy requirements of work.
We expose it as both
a foundation
and C-level APIs
including dispatch.
It lets you, your goal just
kind of immediately walking
out of the session, should be
to try to classify long running
or resource intensive
parts of your application.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or resource intensive
parts of your application.
And this can get you the biggest
bang for the buck both in terms
of responsiveness benefits
and energy improvement.
And you want to try
to aim for 90 percent
of your application's execution
to be at Utility or below
when the user isn't
actively interacting
with your application.
This is kind of a
high-level overview.
There's a lot more details
including discussion
of the dispatch-based APIs
that you can use for this
at the Power Performance
and Diagnostic session
tomorrow afternoon.
I highly encourage
you to attend that.
So, that was Do It
More Efficiently.
So, let's talk about Do It Less.
So, you're running your
code at the right time.
You're not doing work.
You don't need to.
You're doing it with
Quality of Service specified.
Now, how do you just simply
make your code do less work?
So, we're going to talk
about three things.
CPU, Graphics, and Storage,
and techniques you can use
to improve your efficiency
in each of these areas.
But before we talk
about a specific topic,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But before we talk
about a specific topic,
how do you just generally
monitor your energy consumption?
Well, if you are debugging your
application in Xcode and you go
to Debug Navigator, there's
the energy impact gauge.
This is an indication of how
much energy your app is using,
taking into account
a variety of factors.
And you can see in this case,
our application has high energy
impact that is unexpected or bad
if we don't think we're
doing something that's
energy intensive.
And so, you want to keep an
eye on the energy impact gauge,
and what your application is
scoring on as you're developing,
to look for things that
are unexpectedly expensive.
So, with that, let's
dive into CPU.
So, why is it important
to reduce CPU use?
Well, if I have 1
percent CPU use,
I'm going to cause 10 percent
higher power draw from the CPU.
If I have 10 percent CPU use, I
will cause two times the amount
of power draw compared
to an idle CPU.
And finally, if I have
100 percent CPU use,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And finally, if I have
100 percent CPU use,
I will cause 10 times
the power draw.
And so, in this way, your use of
the CPU can have a huge impact
on the amount of power
consumed by your application.
Now, if we go back to
our Debug Navigator,
we can look at the CPU gauge
to see how much CPU our
app is using over time.
This is another thing you
want to try to monitor
as you're developing your
app, to look for places
where you're using
unexpected amounts of CPU.
If you do find you're using
more CPU than you expect,
your best friend is going to
be Instruments Time Profiler.
There's a variety of
talks and guides online
on how to use Time Profiler.
But the short of it is
that it lets you see
where your application is
spending its time to look
for CPU-intensive
routines that you can try
to optimize or eliminate.
One feature that's new in Xcode
6 is Performance Unit Tests.
This is an addition
to the XCTestCase API,
and it helps you find
performance regressions
in your code by measuring
the performance
of particular parts
of your application.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I mentioned before performance
and energy go hand in hand.
And so, making sure you're
doing good performance testing
of your code can help you
find energy regressions early.
API is pretty simple.
You simply call measureBlock
and tell it what code
you want to measure.
And then you'll get a
variety of performance metrics
from that code, including, for
our purposes here, wall time.
If you want to learn more
about this, you should check
out Testing in Xcode 6
and Continuous Integration
with Xcode 6 tomorrow, and
I'll have all these at the end
of the presentation as well.
So, that's reducing CPU use.
And remember that CPU has a
huge dynamic range in power.
Monitor CPU with the
Xcode debug gauge
as you're developing
your application.
Profile your code with
Instruments to find
and eliminate CPU
hogging routines.
And use performance unit
tests to prevent regressions.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, reducing the amount
of CPU use is important,
but it's also important
to consider how you use it
in the context of fixed costs
that we discussed earlier.
So, you want to make sure
you minimize timer use
in your application.
There are lots of
timer APIs in a system.
Everything from NSTimer,
Grand Central Dispatch timers,
CVDisplayLink, all these
APIs will cause timer wakeups
to happen in your application.
And so it's important to
understand how you're using them
and make sure to minimize
your use of these sorts
of APIs as much as possible.
And if we go back to one of our
power graphs, if we imagine each
of these small units of work
is actually a timer firing
in your application, we now
pay all of this fixed cost just
to service these
very small timers.
Who knows whether these are
actually important or not?
So, what you can do is use the
Energy Impact Gauge in Xcode
and look at the wakes
in CPU area
to see how often your
application is waking due
to a timer firing.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you find this is
higher than expected,
you can use the timerfires
command line tool.
And in this case, we'll
want it with a dash-g option
that gives us a summary
of the timers
that fired in our application.
So, you start this tool,
run your application
for a little while, kill
the tool with Control-C,
and then you'll get
a list of the timers
that fired while the tool was
running in your application.
In this case, we can see we
had a lot of calls to sleep,
some dispatch timers and
what routine they called,
and some CF timers, what
routine they called.
Now, reducing timers is great.
Sometimes, you can't
eliminate a timer completely.
In that case, you want to
help the system do a good job
of scheduling that timer
in energy efficient time.
This includes just something
we call Timer Coalescing.
This is a feature we introduced
in last year's releases.
And what this does
is take timers
that are firing the
system and coalesce them
to fire at the same time.
In this way, we can
keep the system
in an idle state for longer.
And we will try to do this
as best we can but we have
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And we will try to do this
as best we can but we have
to subject it to
some limitations
to make sure we don't delay a
timer more than is appropriate.
But you can help us
determine the amount
of tolerance you're willing that
time, for that timer to have
by specifying a timer tolerance
whenever you create a timer.
So, this first example
is for NSTimer.
You can simply call setTolerance
and indicate how much
tolerance you're willing
for that timer to have.
In this case, we
specified 60 seconds.
For a CFRunLoopTimer,
you can call
a CFRunLoopTimerSetTolerance.
And then for a dispatch
source timer,
when you call dispatch
source set timer,
that last parameter
is a tolerance value.
And so, in all these
cases, what we're indicating
in the system is that we're
willing for this timer to take
up to an extra 60
seconds to fire
if that will improve
our energy consumption.
So, let's minimize timers.
Be mindful of the
wakeup overhead timers.
Monitor your app for wakeups
using the Xcode Debug Gauge.
Debug with timerfires
if you find more wakeups
than you expect, and make sure
you specify a timer tolerance
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
than you expect, and make sure
you specify a timer tolerance
whenever you create a timer.
Now, this is a very brief
introduction to timers.
If you want a lot more details
and some examples of good
and bad timer-related code,
check out last year's
Energy Best Practices talk.
So, next I want to talk
about efficient Graphics.
Why is Graphics an
important area?
Well, let's say you
have an application
and your app does some drawing.
Well, that drawing now needs
to get handed to the system
so that it can compute how
that drawing affects the overall
user interface, do some work
in core animation
or core graphics.
Eventually, we'll hand
that work off to the GPU.
That GPU now needs to wake up
out of its low power states
and do some processing.
And then, we eventually
have to hand that off
to the display itself,
which might also be
in a low-power state
and it has to wake up
and do work to update the UI.
And so, it's very important
that we limit our screen
updates as much as possible.
This includes avoiding updating
the screen when we don't need
to because unnecessary drawing
can kick the graphics hardware
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to because unnecessary drawing
can kick the graphics hardware
out of low-power modes.
It also means avoiding drawing
more content than needed.
So, if you get a, if
you have a custom view
and you get a DrawRect
call for a very small area,
make sure you don't draw more
than the area you're required.
And you can use needsToDrawRect
or getRectsBeingDrawn:count
to fine tune what areas
you update in your view.
And there's a great view drawing
guide available and we'll go
into that in more
detail if you're curious.
So, how do you determine whether
you're doing the right thing
with respect to drawing?
Well, you can use
Flash Screen Updates.
So, this is the Quartz
Debug utility that comes
with Graphics tools for Xcode.
If you run this and check the
Flash Screen Updates box-I'm not
going to demo this for you
because it can be a little
bit seizure inducing.
But, if you were to look at the
Shut Down dialog, you would,
on OS X Mavericks, you'd see
the Shut Down button flashing
at 30 frames per second.
And this is correct
behavior in this case,
because that button pulses.
And so, as long as you see
a flash only when we expect
that button to be updated
and that the region flashed,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that button to be updated
and that the region flashed,
which is indicated in yellow,
is only the area
immediately surrounding
that UI element that's changing,
then we're behaving correctly.
On the other hand, if you'd
seen the entire window flash,
that would have been
a cause for concern.
Now, if you were to compare
this to OS X Yosemite,
you'd see that the Shut Down
button doesn't pulse anymore,
so you wouldn't get
any flashing.
And we've improved
the energy consumption
of the Shut Down dialogue.
Now, for iOS, we, if you go to
the Core Animation instrument,
there's a debug option
for Flash Updated Regions
that you can check.
And this will produce
similar behavior
on your tethered iOS device.
And also, one other
thing you want to keep
in mind is visual effects.
So, with the new UI in OS
X Yosemite or the iOS 7 UI,
you might want to
place a translucency
or blur effect on some element.
But you want to make sure
you avoid placing these
over frequently updating
elements.
Because if something
underneath that effect changes,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Because if something
underneath that effect changes,
you have to update
the, and redo the work
to do the blur and translucency.
So, you can imagine, if we were
to put Animating
Content underneath the
NSVisualEffectView, we
would magnify the power cost
of updating that
Animating Content.
On the other hand, if
you move that content
out from underneath
the EffectView,
then you won't see dramatically
increased cost due to that.
So, that's Efficient Graphics.
Make sure to draw
minimally and efficiently.
Monitor your drawing with
Quartz Debug or Instruments
and avoid blurs on
updating content.
And finally, Flash Power.
So, Flash is a little
bit of a different beast
than the rotating hard drives
you might have been familiar
with in older Macs.
In particular, writes to Flash
are significantly more energy
hungry than reads.
So, you want to make sure
you write the minimum amount
of content necessary and
do writing in aggregate
to help amortize some
of those fixed costs.
Also, remember that any I/O you
do can pull the storage device
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Also, remember that any I/O you
do can pull the storage device
out of a lower-power state.
And so, you want
to take advantage
of the I/O caching
available to you
to ensure you're not doing
lots of sporadic I/Os
to keep the device from idling.
So, that was Do It Less.
Things you want to
remember, profile
and monitor your CPU use.
Reduce your timers.
Be efficient in the use of
graphics and minimize your I/O.
All right.
So, to summarize what
we talked about today,
improving your app's energy
consumption improves the
user experience.
Keep in mind how you're
using the device and how,
what kind of impact that
will have on energy.
Make sure you continuously
monitor your app's energy
and resource consumption
as you're developing.
And look for ways to apply the
four techniques we talked about.
Do It Never, respond to
changes in your app's active
or occlusion state to minimize
the amount of work you do
when that work won't
be visible to the user.
Do It at a Better Time, let the
system schedule work using the
NSBackgroundActivityScheduler or
NSURLSessionBackground Session.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
NSBackgroundActivityScheduler or
NSURLSessionBackground Session.
Do It More Efficiently, specify
Quality of Service Classes
on your work-and this can
provide huge benefits both
in energy and responsiveness-and
Do It Less.
Optimize and improve
your resource use.
For more information, you
can contact Paul Danbold,
the Core OS Evangelist.
You can also check out
these talks from last year,
Energy Best Practices and
Building Resource Efficient Apps
for more depth in some of
the things we covered today.
There's a variety of related
sessions I'd highly encourage
you to check out.
Yesterday's What's New in
Foundation Networking talk went
in more depth about
the Background Session.
Improving Your App with
Instruments is a great session
to learn more about
Instruments and some
of the new features available.
Writing Energy Efficient Code,
Part 2, right here on this stage
in just a few minutes.
Testing in Xcode 6 and
the Continuous Integration
with Xcode 6 will
go into more depth
about the performance
unit tests.
Fix Bugs Faster Using Activity
Tracking is a great way
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Fix Bugs Faster Using Activity
Tracking is a great way
to help understand the
asynchronous work your
application is doing.
And, of course, Power,
Performance, and Diagnostics,
tomorrow afternoon, will go into
much more depth about Quality
of Service Classes and
how to specify them
in a variety of layers
in the API.
Thank you.
[ Applause ]