Wednesday, December 30, 2015

Writing Energy Efficient iOS Code



How to extend the user's battery life and improve user experience.

This transcript is from Apple's WWDC14 2-part video: "Writing Energy Efficient Code"

Overview

OSX 10 Maverix and UP, 2 new sections were added:
1. "Apps Using Significant Energy" in the battery indicator on the status bar.
2. "Battery Usage" tab in Settings showing by-app breakdown of battery usage.


Everything on the system uses Energy. Components include:
1. CPU (using a small amount of CPU makes a big difference to energy consumption)
2. Flash storage. (big dynamic range)
3. Networking.
4. Graphics. (A small change by the developer might cause an expensive operation under the hood)


There are 3 states of energy use:
1. Idle power (device is not being used.  apps not running)
2. System Active (your app is running code)
3. Intermediate States. (system is idle, but not able to get back to idle power.  Time is required to achieve this state.)
a. Device stays in this state a lot, if sporadic work is done.
b. This is the concept of "Fixed Cost".  Anytime sporadic work needs to be done, a minimal cost is incurred.
c. Fixed cost tasks (sporadic tasks) consume a lot of energy for the amount of work they actually perform. (high overhead)
d. The solution is: Bundle all the sporadic tasks together in order to reduce the fixed cost of your work.


Energy and Power are 2 different things.
Power is the peak value at a given, instantaneous point in time (measured in Watts)
Energy is the sustained combined energy usage to accomplish a task (measured in Joules)+

Better Performance most times means: Better Energy use

For small workloads, Fixed Cost will dominate
For intensive workloads, dynamic cost will dominate

POWER can be TRADED for ENERGY, for example:  a single threaded workload can be multi threaded in order to minimize the Fixed Cost. (The Dynamic Cost will stay the same)



Techniques:

1. Do It Never  (Avoid unnecessary work)

If another app comes in front of your app, are you still running animation code in your app?
If another view comes on top of the active view, are you still animating the parent view?
Use:
- (void)applicatioNDidResignActive
- (void)applicationDidBecomeActive
or listen for UIApplicationWillResignActiveNotification

Pause Timers and animations when resigned active.


2. Do It a a Better Time

If you expect an expensive operation such as downloading a huge update or content.  Schedule the work when the user is plugged in to a power source.
OS10 Yosemite has NSBackgroundActivityScheduler schedule things like:
- Periodic content fetch
- Update install
- Garbage collection and data maintenance tasks
- Automatic saves or backups

OSX:
1. Create an NSBackgroundActivityScheduler object with a reverse-dns Unique Identifier.  Re-use these identifiers for the same activity.
2. Set the tolerance  (in seconds.  600 = 10 minute from now start)
3. Set the interval (Execute every interval.  Tolerance applies on top of this)
4. Set reapeats = YES (if you want it to repeat)
5. shouldDefer (allows deferring work until the best time)
Interval will execute it once each Period (not at the same time each time).

Call activity scheduleWithBlock:...

NSURLSession Backround Session:
ou pass a bunch of NSURLRequests to the background session, and they will get executed Out-Of-Process on a System Daemon.
Delegate callbacks work as usual with NSURLSession
If your app gets terminated, your tasks will still be executed by the OS, and when App is re-opened, you will
retrieve your existing session by it's unique string ID, and you will get delegate callbacks also.

iOS7 and Up, you can set configuration.discretionary = YES;   (System picks the best time to do the work)
This provides bandwidth monitoring and automatic retry. (if bandwidth drops below minimum speed, task is paused and retried later. Handles edge cases for you)



3. Do It More Efficiently

iOS8 and up, has "Quality of Service Classes"
1. User Interactive (main thread interactions) - Is this work actively involved in updating the UI? Animations, input event processing...
2. User Initiated (Immediate results) - Is the user waiting on this content before next Interaction can be done.
a. Is it OK for Usre Interactive work to happen before my work?
b. Is it okay for this work to complete with other User Initiated work?
c. Is it oka for my work to take precedence over Utility work?
3. Utility (Long-running tasks) - Is the user aware of the progress of this work. Does it show a progress bar?
4. Background (Not user visible)

(This is a hierarchical priority)
The system prioritizes higher level tasks over lower level tasks..


Practical Example: Imagine we have a Grid View app with image thumbnails being imported and loaded:
1. User Interactive would be the Scrolling only (main thread)
2. User Initiated would be the Thumbnail Generation, and Image loading when clicked.
3. Utility would be Image import and conversion (progress bar shown)
4. Background would include search indexing.



Powermetrics tool can be used to see which QoS are in use. 
usage:  sudo powermetrics —show-process-cos —samplers tasks

Gives the MS per sec usage breakdown of each QoS class for your application. 

To see which QoS class is used while debugging: 
  • Pause in debugger. 
  • Go to the CPU tab to see breakdown of threads. 
  • You will see all the different threads and resource usage of each. 
  • For each thread it also shows the QoS class under the thread name. 

Spindump tool can be used to see which QoS is used to execute particular code. 
usage: sudo spindump -timeline MyApplication

Gives you the history of which QoS was used over the lifetime of the execution of your code. 

When the user is not actively interacting with your application, you want to aim to have 90% of all work to be at Utility or Below.  


4. Do it less:

CPU:  
1% CPU usage causes 10% higher use over idle
10% CPU usage causes 2x power draw over idle
100% CPU usage causes 10x power draw over idle. 

  • CPU use has a huge dynamic range in power.
  • Minitor CPU use with Xcode debug gauge
  • Intruments’ Time Profiler is the best tool to use for monitoring CPU usage. Gives a stack trace breakdown by CPU usage. 
  • Performance Unit Tests can be used to detect Performance/Energy regressions.  

Minimize Timer Use in the application:  to minimize the Fixed cost overhead associated with them.
Timer types:
  • NSTimer
  • CFRunLoopTimer
  • pthread_cod_timedwait()
  • sleep()
  • GCD timers
  • select()
  • CVDisplayLink
  • dispatch_semaphore_wait()

Use the # Wakes tab in the Energy Impact tab to find out how many times the app awoke due to a timer firing on average per second. 

sudo timerfires -p MyApplication -s -g 
Helps see all timer firings inside the app. 

Timer Coalescing:  Groups timer executions together, according to a maximum tolerance you specify.  Examples:
[myTimer setTolerance: 60.0];

CFRunLoopTimerSetTolerance(myTimer, 60.0);

dispatch_source_set_timer(my_timer, DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC, 60 * NSEC_PER_SEC);

This grouping of timer executions improves the fist cost of energy consumption. 
  • Be mindful of wakeup overhead
  • Monitor for wakeups
  • Debug with timefires
  • Specify timer tolerance
Graphics:
  • Avoid extraneous screen updates
  • Unnecessary drawing kicks graphics hardware out of low-power modes
  • Drawing more content than needed causes extra power draw to update the screen
  • Use needsToDrawRect: or getRectsBeingDrawn:count: methods to fine-tune drawing

In Debug options you can enable: “Flash updated regions” to visually see which regions are being updated, to check whether all those updates are necessary. 

If you apply a translucency or blur effect on an element that has other elements behind them that frequently update, this is an expensive graphical operation.  Better to ensure that frequently updated content is not behind any elements that have transparency effects.  (Avoid Blurs on updated content)


Storage:
  • Writes to flash are much more energy hungry than reads
  • Write the minimum content necessary
  • Aggregate the writes for better efficiency. 
  • Any I/O will pull device out of low-power states. 
  • Use caching to your advantage. 


Writing Energy Efficient Code Part 2


Recap: 

The Fixed cost of any resource is due to the fact that the resource stays powered on for a while after performing work, in case its’ needed for more work in a short period of time.  If no work is performed, then the resource is powered back down after a while.   

Trading Power for Energy is generally a concept of Using higher power in a shorter amount of time in order to minimize the Fixed cost of the work performed.  (thus reducing the total energy used) - “Do as much work as you can quicker, minimizing the overall energy used”. 

  • Do it never
    • Stop unnecessary work on app transitions
  • Do it at a better time
    • Scheduling with NSBackgroundActivityScheduler / NSURLSession
  • Do it more efficiently
    • Set appropriate QoS work priority
  • Do it less
    • Coalesce your timers

CPU Monitor:
Throws an exception if over-normal type of CPU usage is observed.
Example:
Exception Type: EXC_RESOURCE
Exception Subtype: CPU_FATAL
Exception Message: (Limit 80%) Observed 89% over 60 seconds. 
Catches “Runaway background usage"
But this won’t catch normal use which is happening in background, in times that it should not be.   

Energy efficient Animations:
Review your Blur usage and reduce frame changes behind blurs. 
Avoid extraneous screen updates. 



Energy Efficient Networking

  • Lots of small calls has the Fixed cost of keeping the radios ON over a period of time  (Overhead costs).   In this case, the overhead cost is very high in proportion to the amount of packets sent. 
  • Not all means of network transfer are the same.  For example: 3G web browsing for example is more expensive than WiFi. 
Conditions affecting network battery use:
  • Celliar vs WiFi 
  • Signal conditions
  • Network throughput

Solution 1:
  • Buffer data together and send as batch.   (Coalesce transactions)

Do it less/never 
  • Reduce media quality
  • Compress data
Avoid redundant transfers
  • Cache data
  • Resumable transactions  (or chunked transfers
Handle errors:
  • Timeout
  • Retry policies
Consider tolerance (doing it at a better time)
  • Understand the requirements - when really is it needed?
  • Consider technology used
  • Check network conditions before sending. 
  • Check data before sending it to make sure something actually changed. 
NSURLSession allows:
  • Pause/Resume
  • Caching with (NSURLCaching)
  • Background Sessions - out of process transactions
NSURLSession Example:
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: @“com.apple.App.UserRestore”];
[config setAllowsCellularAccess:NO];   // Will only use WiFi - save the user money
[config setDiscretionary: YES];   // Take care of the task at any time within the window
config.timeoutIntervalForResource = 18 * 60 * 60;  // Within the last 18 hours. 
…  Now make the session and task with this config as normal.  

Summary: 
  • Coalesce your transactions
  • Coalesce your transfers
  • Consider tolerance
Measuring Impact:
  • Developer menu on the device now has an Instruments option where you can start recording an energy trace.
  • Then “Import data from device” to load this file .
  • Apps can also force display brightness to maximum. Make sure this is only done when absolutely necessary. 
Sleep:
“To sleep is to prepare for the longer journey ahead”. 
“The longer you allow your devices to sleep, the better the battery life”. 

Background best practices:
Notifications:
Device does wake up in order to send the local notification or push notifications. 
  • With Push Notifications, you can set the push priority.  (10 is default - immediately. 5 - Delivered at a power conservative time). 

VoIP

Previous to iOS 8 was Periodic keep-alive packets causing device wakes, and code complexity. 
As of iOS8 the PushKit framework now allows using the Push Notification service to talk to VoIP apps:
  • No persistent connection required. 
  • app relaunched if terminated
  • include up to 4k payload  (much more than the 256 bytes with regular pushes)
  • app runtime to process the pushes. 

VoIP Push example:
#import <PushKit/PushKit.h>

- (void) voipRegistration {
PKPushRegistry *voipRegistry = [PKPushRegistry alloc] initWithQueue: dispatch_get_main_queue()];
voipRegistry.delegate = self;
voipRegistry.desiredPushTypes = [NSSet setWithObject: PKPushTypeVoip]; // register

}

NOTE: VoIP Background mode needs to be requested for this to work. 

Delegate methods:
  1. Handling push tokens
- (void) pushRegistry:(PKPushRegistry*)registry didUpdatePushCredentials:(PKPushCredentials*)credentials forType:(NSString*)type
{
    // Register push token with server. 
    // There will be a separate token between VoIP and Push notifications. 
}

- (void) pushRegistry:(PKPushRegistry*)registry didReceiveIncomingPushWithPayload:(PKPushPayload*)payload forType:(NSString*)type
{
    // Received push
}

On the server:
  1. Request the VoIP push certificate on the apple portal
  2. iOS8+ only.  

Location Optimizations:
  • Example: Location based restaurant suggestions. 
  • [locationManager startUpdatingLocation] will keep your device ON continuously. 
  • Accuracy affects energy use. (more precise is more energy used)
  • Only use this when necessary, and turn off continuous updates when not needed. 
  • Make sure to Turn OFF location Updates when not needed. 

If you don’t need GPS level accuracy: 
  • locationManager allowDeferredLocationUpdatesUntilTravelled:timeout: 
(Only notifies if you moved a certain distance)  500 meters or 5 minutes for example. 
Example: Weather app. 

Region Monitoring:
  • When entering or exiting a specific location.  
  • Set up a specific region you care about.  App is only woken up when condition is satisfied. 

Significant Locations Visited API

BLE

Peripheral-side buffering can be used to only wake the device when the peripheral’s buffer is full. 
Can also group unrelated transfers into one wakeup occurrence, such as a BLE transfer at the same time that Location entry is satisfied.
Learn how to create your own Programming Language here!!

The Mobile Startup: Episode 5: Some thoughts about tech, and work.

Knowing that You're Bad! I think that if you have never thought of yourself as a bad engineer before, then you are probably a Bad e...