Current attempts at solving the programming challenges associated with multicore processors are based on established ideas and research: The Actor model, task queues, data flow parallelism and so on were all invented some thirty years ago, but are enjoying renewed interest because of the need to harness the power of multicore processors.
Apple's Grand Central Dispatch, introduced in the most recent version of OS X, is based on such a venerable idea as well—task parallelism—but offers novel features thanks to its deep integration into the operating system. According to an overview paper [PDF document] published by Apple:
GCD shifts the responsibility for managing threads and their execution from applications to the operating system. Mac OS X Snow Leopard provides APIs for GCD throughout the system, and uses a highly scalable and efficient runtime mechanism to process work from applications...
GCD gives developers a simple way to describe the different pieces of work that your applications need to do, and an easy way to describe how those pieces might be dependent upon one another. Units of work are described as blocks in your code, while queues are used to organize blocks based on how you believe they need to be executed. By using blocks and queues, you no longer need to worry about threads, thread managers, or locking data, making an application’s code easier to understand. You can simply let the system manage the work queues and execute the blocks for optimal performance...
GCD has a multicore execution engine that reads the queues created by each application and assigns work from the queues to the threads it is managing. GCD manages threads based on the number of cores available and the demands being made at any given point in time by the applications on the system. Because the system is responsible for managing the threads used to execute blocks, the same application code runs efficiently on single-processor machines, large multiprocessor servers, and everything in between.
In order to define such tasks, GCD provides extensions to Objective-C, C, and C++. Such tasks are essentially lambda expressions, a feature also planned for the upcoming C++0x version of the C++ language, although GCD uses a different syntax to describe such expressions:
Blocks are a simple extension to C (as well as Objective-C and C++) that make it easy for you to define self-contained units of work. A block in code is denoted by a caret at the beginning of a function. For example, you could declare a block and assign it to x by writing:
x = ^{ printf("hello world\n"); }
This turns the variable x into a way of calling the function so that calling x( ); in the code would print the words hello world.
A novel feature of GCD is that it schedules block execution on work queues based on the load characteristics of an entire system. This can be accomplished by GCD being integrated deep into the operating system:
You schedule blocks for execution by placing them on various system- or user-queues. GCD then uses those queues to describe concurrency, serialization, and callbacks. Queues are lightweight user-space data structures, which generally makes them far more efficient than manually managing threads and locks...
Whenever a queue has blocks to run, GCD removes one block at a time and schedules it on the next available thread from the pool of threads that GCD manages. This saves the cost of creating a new thread for each request, dramatically reducing the latency associated with processing a block...
Blocks are added and removed from queues using atomic operations available on modern Intel processors, which are guaranteed to execute completely (without interruption) even in the presence of multiple cores.
What do you think of Apple's OS-centric approach to multicore programming?
Although GCD has its advantages, I wonder how are they going to tackle synchronization. Other solutions (like the Actor model, for example), handles synchronization automatically, without intervention from the programmer. Is the programmer responsible for actually putting each block in the appropriate queue so as that synchronization issues are avoided?
GCD sounds a lot like the Callable/Executor/BoundedQueue classes from util.concurrent.
It sounds like the tuning of those classes is provided automatically by the OS, but I don't see much else that differs.
Achilleas asks the right question. Tasks/executors/queues still require synchronization, and synchronization is what's hard about multi-threaded programming. How is this solved?
This doesn't seem to be portable at all! Using proprietary extensions to the language and proprietary concurrency model and techniques is just a way for Apple to lock users in.
> This doesn't seem to be portable at all! Using proprietary > extensions to the language and proprietary concurrency > model and techniques is just a way for Apple to lock users > in.
Yes, it is indeed shocking that an otherwise quite open company such as Apple would come up with proprietary extensions.
As for the synchronization, the Wikipedia article on GCD (http://en.wikipedia.org/wiki/Grand_Central_Dispatch) has some extra information regarding eventing. It looks like blocks would be able to synchronize by activating each other via events.
> As for the synchronization, the Wikipedia article on GCD > (http://en.wikipedia.org/wiki/Grand_Central_Dispatch) has > some extra information regarding eventing. It looks like > blocks would be able to synchronize by activating each > other via events.
That sounds a bit like Actors: Actors also active each other, and you can in fact implement almost any synchronization construct with Actors. Actors are really about events, in that an Actor-based system defines a partial order of events in which Actors receive messages from other Actors.
> > This doesn't seem to be portable at all! Using > proprietary > > extensions to the language and proprietary concurrency > > model and techniques is just a way for Apple to lock > users > > in. > > Yes, it is indeed shocking that an otherwise quite open > company such as Apple would come up with proprietary > extensions. > > As for the synchronization, the Wikipedia article on GCD > (http://en.wikipedia.org/wiki/Grand_Central_Dispatch) has > some extra information regarding eventing. It looks like > blocks would be able to synchronize by activating each > other via events.
Where does it say that? in the given example of word analysis, what happens if a document is being analyzed long enough for the user to change the document? I don't think there is any synchronization on the data at all.
> > As for the synchronization, the Wikipedia article on > GCD > > (http://en.wikipedia.org/wiki/Grand_Central_Dispatch) > has > > some extra information regarding eventing. It looks > like > > blocks would be able to synchronize by activating each > > other via events. > > Where does it say that?
In the 4th paragraph down:
A block in Grand Central Dispatch can either be used to create a work item that is placed in a queue, or it can be assigned to an event source. If a block is assigned to an event source, when the event triggers, a work unit is made from the block, and the work unit is placed in an appropriate queue. This is described by Apple as more efficient than creating a thread whose whole purpose is to wait on a single event triggering.
> in the given example of word > analysis, what happens if a document is being analyzed > long enough for the user to change the document? I don't > think there is any synchronization on the data at all.
Though it's not explicitly stated one way or the other, I don't see any restrictions on having shared state between blocks. In fact, the parallelizing loop example assumes a shared array. If the API doesn't provide an interrupt mechanism, a long-running block could periodically check a shared boolean value to see whether it should continue running.
- i suppose having an OS-wide scope, GCD can more effectively schedule CPU resources than java.util.concurrent can (which is JVM-scoped)
- i'm appalled by Apple's marketing-heavy language (everything is oh soooo easy and simple) and the proprietary nature of this ad-hoc programming language extension. the latter is perfectly in-line with most of Apple's contributions to software development, though. the former is in stark contrast to the nature of the approach: task parallelism - where one must explicitly identify and package-up parallelisable units of work, analyze their dependencies and queue them accordingly.
p.s.: sorry if i've sounded grumpy but sometimes it feels like software development is just going round in circles while issuing self-aggrandising statements at every round...
> > > As for the synchronization, the Wikipedia article on > > GCD > > > (http://en.wikipedia.org/wiki/Grand_Central_Dispatch) > > has > > > some extra information regarding eventing. It looks > > like > > > blocks would be able to synchronize by activating > each > > > other via events. > > > > Where does it say that? > > In the 4th paragraph down: > > A block in Grand Central Dispatch can either be used to > create a work item that is placed in a queue, or it can be > assigned to an event source. If a block is assigned to an > event source, when the event triggers, a work unit is made > from the block, and the work unit is placed in an > appropriate queue. This is described by Apple as more > efficient than creating a thread whose whole purpose is to > wait on a single event triggering. > > > in the given example of word > > analysis, what happens if a document is being analyzed > > long enough for the user to change the document? I > don't > > think there is any synchronization on the data at all. > > Though it's not explicitly stated one way or the other, I > don't see any restrictions on having shared state between > blocks. In fact, the parallelizing loop example assumes a > shared array. If the API doesn't provide an interrupt > mechanism, a long-running block could periodically check a > shared boolean value to see whether it should continue > running.
What if two or more blocks write the same variable?