Slide 1

Slide 1 text

Concurrent Programming in OS X and iOS Jeff Kelley | @SlaunchaMan www.jeffkelley.org CocoaConf Columbus | August 10, 2012

Slide 2

Slide 2 text

Who?

Slide 3

Slide 3 text

Concurrency • It isn’t enough to go fast • Moore’s Law expiring early • Expanding to multiple processor cores, not faster processors • Manually creating threaded code sucks • Different tools for different jobs

Slide 4

Slide 4 text

Going Fast The complexity for minimum component costs has increased at a rate of roughly a factor of two per year… Certainly over the short term this rate can be expected to continue, if not to increase. Gordon E. Moore, 1965

Slide 5

Slide 5 text

Going Fast • Processors are still getting faster, but it’s slowing down • This was predicted for 2015, but something funny happened along the way • Mobile processors have more stringent heat and power consumption needs

Slide 6

Slide 6 text

Going Fast • Desktop computers are going multicore • A Mac Pro can have twelve processor cores! • The fastest possible algorithm may not matter if it uses a single core

Slide 7

Slide 7 text

Threaded Code Some people, when confronted with a problem, think, "I know, I'll use threads," and then two they hav erpoblesms. Ned Batchelder

Slide 8

Slide 8 text

Threaded Code • Manually-threaded code is horrible to write • Query the number of cores • Ask them how busy they are • Create the appropriate number of threads • Do stuff on those threads, monitoring the cores to see which one to use

Slide 9

Slide 9 text

Yuck.

Slide 10

Slide 10 text

Threading Problems • It’s difficult to gauge current CPU use and impossible to know future use • Two programs each trying to be as multithreaded as possible will fight for resources • Lots of wasted effort and surface area for bugs • Bugs here are harder to track down and potentially extremely nasty

Slide 11

Slide 11 text

Thread Safety • Writing to a portion of memory on one thread while trying to read that portion of memory on another is… problematic. • All kinds of solutions for this • @synchronize(myObject) • Locks, semaphores, etc. • Core Data “thread safety”

Slide 12

Slide 12 text

Thread Safety • This is one problem we won’t solve today. • We will make it better.

Slide 13

Slide 13 text

So What’s a Developer To Do? UNIX Threading Model Grand Central Dispatch NSOperationQueue New Cocoa (Touch) APIs

Slide 14

Slide 14 text

So What’s a Developer To Do? • Stop managing threads on your own • Think of the things your app needs to do as units of work. • Enqueue units of work and let the OS decide how to run them • The OS has a lot more knowledge than your program does

Slide 15

Slide 15 text

Grand Central Dispatch

Slide 16

Slide 16 text

Grand Central Dispatch • C API for managing queues of work • Relies heavily on blocks, an Apple extension to the C language • Manually memory managed • Open-sourced as libdispatch • Generally pretty awesome

Slide 17

Slide 17 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; });

Slide 18

Slide 18 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; });

Slide 19

Slide 19 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; });

Slide 20

Slide 20 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; });

Slide 21

Slide 21 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; });

Slide 22

Slide 22 text

Simple GCD dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_ PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [self performLongTask]; });

Slide 23

Slide 23 text

Basic Dispatch Functions • dispatch_async(queue, block); dispatch_async_f(queue, context, func); • Schedules block or function on queue, returns immediately • dispatch_sync(queue, block); dispatch_sync_f(queue, context, func); • Schedules block or function on queue, blocks until completion

Slide 24

Slide 24 text

Dispatch Queues • dispatch_queue_t • Main Queue • Analogous to main thread (Do your UI operations here) • dispatch_get_main_queue()

Slide 25

Slide 25 text

Global Queues • dispatch_get_global_queue(priority, flags); • priority is one of four constants: • DISPATCH_QUEUE_PRIORITY_BACKGROUND • DISPATCH_QUEUE_PRIORITY_LOW • DISPATCH_QUEUE_PRIORITY_NORMAL • DISPATCH_QUEUE_PRIORITY_HIGH • flags arg should always be 0 (for now)

Slide 26

Slide 26 text

Making Queues • dispatch_queue_create(label, attr) • Use reverse DNS for label • com.example.myQueue • attr defines the type of queue • DISPATCH_QUEUE_SERIAL • DISPATCH_QUEUE_CONCURRENT • Be sure to use dispatch_release()

Slide 27

Slide 27 text

Using Queues • The main queue is serial • First-in, first-out, one at a time • Global queues are concurrent • GCD automatically chooses how many (usually # of CPU cores) • You pick for queues you create

Slide 28

Slide 28 text

Queues To Control Access • Easy way to limit access to a piece of memory • Create a serial queue (one-at-a-time, FIFO) for the object • All access to the object goes through this queue • No lock required!

Slide 29

Slide 29 text

Typical GCD Pattern • dispatch_async() with a background queue to kick off work • dispatch_async() with the main queue to display the results

Slide 30

Slide 30 text

Demo

Slide 31

Slide 31 text

Grand Central Dispatch • Useful for more than just threading! • Can be used to replace the main run loop in your app • For a good, lightweight example of a C program using GCD, check out the source to Mountain Lion’s caffeinate utility • Can support timers and file notifications

Slide 32

Slide 32 text

Grand Central Dispatch • Manages threads for you, uses as many as it needs • Not the most user-friendly API in the world • No way to cancel a task • No way to adjust the priority of a task • Memory Management?!?

Slide 33

Slide 33 text

NSOperationQueue

Slide 34

Slide 34 text

NSOperationQueue • Much like GCD, you enqueue units of work onto queues • Unlike GCD, the units of work and the queues themselves are Objective-C objects • NSOperation and NSOperationQueue

Slide 35

Slide 35 text

NSOperationQueue • Operations can have priority amongst one another • [myOperation setQueuePriority:NSOperationQueuePriorityLow]; • Operations can depend on one another • [myOperation addDependency:myOtherOperation]; • Even across different queues!

Slide 36

Slide 36 text

NSOperationQueue • Operations are cancellable • [myOperation cancel]; • In your custom operation class, check for the canceled property

Slide 37

Slide 37 text

Custom Operation Class? • Two ways to create an operation • NSBlockOperation • Create an operation with a work block • Subclass NSOperation • Implement -main with your custom logic

Slide 38

Slide 38 text

Why Subclass • Gives you a pointer to self to call [self isCancelled] • Asynchronous operations • URL loading, geocoding, etc. • The end of main does not necessarily end the operation • Implement -start and -isFinished

Slide 39

Slide 39 text

Demo

Slide 40

Slide 40 text

NSOperationQueue • Objective-C class to manage the execution of units of work • Create custom operations to perform a unit of work • With ARC, you don’t need to worry about memory management • Can cancel and prioritize tasks

Slide 41

Slide 41 text

New Cocoa (Touch) APIs

Slide 42

Slide 42 text

New Cocoa (Touch) APIs • Sometimes you don’t want to worry about managing threads, dispatch queues, or operation queues • Common, repetitive tasks that could be made faster with concurrency, but it’s not worth the effort to create a queue and manage it • Apple wants you to write fast code

Slide 43

Slide 43 text

New Cocoa (Touch) APIs • Enumerating a Collection • Sorting an Array

Slide 44

Slide 44 text

Enumerating a Collection • A task as old as programming itself • Walk the collection, item-by-item, and do something with each one

Slide 45

Slide 45 text

Enumerating a Collection NSUInteger count = [myArray count]; for (int i = 0; i < count; i++) { id obj = [myArray objectAtIndex:i]; [obj doSomething]; }

Slide 46

Slide 46 text

Enumerating a Collection NSUInteger count = [myArray count]; for (int i = 0; i < count; i++) { id obj = [myArray objectAtIndex:i]; [obj doSomething]; for (j = 0; j < [myNewArray count]; j++) { // More code inside this loop! } }

Slide 47

Slide 47 text

Enumerating a Collection NSEnumerator *enum = [myArray objectEnumerator]; id object; while ((object = [enum nextObject])) { [object doSomething]; }

Slide 48

Slide 48 text

Enumerating a Collection NSEnumerator *enum = [myArray objectEnumerator]; id object; while ((object = [enum nextObject])) { [object doSomething]; NSUInteger i = [myArray indexOfObject:object]; }

Slide 49

Slide 49 text

Enumerating a Collection for (id object in myArray) { [object doSomething]; }

Slide 50

Slide 50 text

Enumerating a Collection size_t count = [myArray count]; dispatch_queue_t queue = ... dispatch_apply(count, queue, ^(size_t i) { id object = [myArray objectAtIndex:i]; [object doSomething]; });

Slide 51

Slide 51 text

Enumerating a Collection [myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { ! [obj doSomething]; }];

Slide 52

Slide 52 text

Enumerating a Collection [myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { ! [obj doSomething]; }];

Slide 53

Slide 53 text

Enumerating a Collection [myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { ! [obj doSomething]; }];

Slide 54

Slide 54 text

Enumerating a Collection • Concurrency for free! • Don’t worry about queue management • Very quickly add concurrency to an existing project

Slide 55

Slide 55 text

Sorting a Collection • NSArray and NSOrderedSet collections sometimes need sorting • Many, many algorithms • The more objects in the collection, the more time it’s going to take—potentially exponentially

Slide 56

Slide 56 text

Sorting a Collection NSMutableArray *myArray; [myArray sortWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(id obj1, id obj2) { ! ! ! return [obj1 compare:obj2]; ! ! }];

Slide 57

Slide 57 text

Sorting a Collection NSMutableArray *myArray; [myArray sortWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(id obj1, id obj2) { ! ! ! return [obj1 compare:obj2]; ! ! }];

Slide 58

Slide 58 text

Sorting a Collection NSMutableArray *myArray; [myArray sortWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(id obj1, id obj2) { ! ! ! return [obj1 compare:obj2]; ! ! }];

Slide 59

Slide 59 text

Sorting a Collection NSMutableArray *myArray; [myArray sortWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(id obj1, id obj2) { ! ! ! return [obj1 compare:obj2]; ! ! }];

Slide 60

Slide 60 text

Sorting a Collection • Stop worrying about sort algorithm (for most applications) • Utilize as many cores as needed to sort your data • Huge returns as hardware increases in throughput

Slide 61

Slide 61 text

Thread Safety • Don’t modify objects from multiple queues • Use dispatch queues to coordinate access • Use the main dispatch and operation queues for UIKit operations • Assume Apple code is not thread-safe

Slide 62

Slide 62 text

GCD Barriers • Great tool for thread safety • Allow for concurrent reading of data but serial writing • For instance, read from a dictionary on any queue simultaneously, write to it on a single queue

Slide 63

Slide 63 text

Demo

Slide 64

Slide 64 text

Thread Safety and Core Data • Create a separate Managed Object Context for each queue • Don’t pass NSManagedObject instances between queues • Use the object ID instead • Register for the NSManagedObjectContextDidSaveNotification notification

Slide 65

Slide 65 text

Wrap-Up • Concurrency is an enormous topic • Thread Safety is its own talk, especially if you use Core Data • Concurrency is not magic performance snake oil • Concurrency does help you take advantage of hardware enhancements

Slide 66

Slide 66 text

For More Info • http://jeffkelley.org • @SlaunchaMan • github.com/SlaunchaMan • “Performance Tuning” session by Mark Dalrymple tomorrow morning • Learn Cocoa Touch