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