Core OS • iOS, OS X • 49:24
People expect long battery life on both their iOS devices and Mac laptops and your apps play a vital role in ensuring long battery life. Get an overview of energy efficiency best practices and learn about new APIs in iOS and OS X that help you to minimize the energy impact of your code.
Speaker: Anthony Chivetta
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
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. 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 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 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. 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 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 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. 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 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 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, 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.
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 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. 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 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 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, 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, 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.
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. 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 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 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? 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, 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 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. 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, 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 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, 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.
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. 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? 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 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 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, 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.
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. 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, 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, 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. 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. 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.
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 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 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 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, 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, 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 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.
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 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 ]