New .NET libraries support data and task parallelism.
- By Stephen Toub
In recent years, hardware manufacturers have been unable to translate Moore's Law into the approximately 50 percent increase per year in clock speed that we've become accustomed to seeing, and industry expectations are extremely low that we'll see such gains again in the foreseeable future. Instead, hardware manufacturers are beginning to increase the number of computational cores in both client and server machines at rates keeping with Moore's Law. Today, it's rare in the United States to find a desktop computer on sale with fewer than two cores, with quad-core being commonplace. And within a few years, industry experts expect to see eight-core and 16-core boxes as the norm.
Unfortunately, most software today is written in a largely serial manner, resulting in only single-core utilization, meaning that increases in the number of cores will have little positive impact on the performance of that software. To address this, we as an industry need to start parallelizing the software we write, such that workloads can be spread across available computing resources in order to take full advantage of a machine's power. Today, writing such code is quite challenging; it's a goal for Parallel Extensions to .NET Framework to make it significantly easier.
PLINQ and TPL
Parallel Extensions is a new set of libraries to be included in .NET Framework 4.0 and Visual Studio 2010 (a community technology preview of Parallel Extensions is available for download here). Through several encompassed components, these libraries enable both data parallelism (where the same operation is executed against multiple, discrete data elements) and task parallelism (where multiple, potentially divergent and unrelated operations are performed in parallel).
The first component is Parallel Language Integrated Query, also known as Parallel LINQ, or PLINQ for short. LINQ, which was introduced with .NET Framework 3.5, enables a declarative model for specifying operations to be performed on data, such as projections, filters, reductions and sorts, all of which are surfaced through a set of "Standard Query Operators." These operators are available through extension methods as well as through query comprehensions in the C# and Visual Basic (VB) languages. PLINQ extends LINQ by substituting parallelized implementations of these operators, enabling LINQ queries to be parallelized with minimal effort from a developer-in many cases, all it takes is adding ".AsParallel()" into the query.
Another piece of Parallel Extensions is the Task Parallel Library (TPL). TPL provides parallelized looping constructs, including a Parallel.For and a Parallel.ForEach, meant to be used in place of "for" and "for each" language constructs in languages like C# and VB. It also provides Parallel.Invoke, which can be used to efficiently parallelize blocks of statements. PLINQ and these Parallel constructs in TPL sit on top of a lower-level API set that forms the bulk of TPL and that enables efficient, fine-grained task parallelism. This set is formed around a Task class that represents an asynchronous operation that can be manipulated with a variety of operations, including being waited on, canceled and continued from. TPL also includes support for tasks that return results, for running tasks on isolated schedulers and much more.
Parallel Extensions is rounded out by a core set of coordination and synchronization types that are important to writing efficient, multithreaded applications (in fact, many of these types are used within the implementation of both PLINQ and TPL). This includes a multitude of scalable, thread-safe collections, such as a dictionary that enables multiple concurrent readers and writers. It includes low-level types for doing efficient spin waiting, on top of which a SpinLock has been built and provided. It includes types for doing thread-safe lazy initialization. And it includes synchronization primitives, such as barriers and countdown events for coordinating concurrent operations.
[click image for larger view]
|PLINQ parallelizing an example query: (from x in D.AsParallel() where p(x) select x*x*x).Sum();
Some developers may be familiar with Microsoft's Concurrency & Coordination Runtime (CCR), available with Microsoft's CCR and DSS Toolkit 2008. Parallel Extensions and the CCR provide distinct programming models targeting different needs, and in this manner are complementary technologies. TPL is well-suited for synchronous parallelism and common patterns such as parallel loops, enabling the efficient parallelization of long-running, synchronous computations. Applications that use LINQ to query in-memory data can use PLINQ to parallelize those queries and get substantial performance improvements on multi-core hardware with minimal code changes. The CCR provides primitives for loosely coupled applications based on in-process message passing between in-memory queues; it's well-suited to orchestrate concurrent work streams within asynchronous I/O-driven apps.
Microsoft is supporting parallelism for more than just .NET. The next version of Visual C++ sports significant new functionality for developers writing parallel applications in native code, and Visual Studio 2010 will include enhanced tooling support to improve developers' abilities to debug and profile concurrent code.
Stephen Toub is a senior program manager lead on the Parallel Computing Platform team at Microsoft.