PLINQ - Doing More with Less


Published on

Have you ever wanted to code less and get more work done? Wouldn't it be cool if the tools we used just understood what we wanted to do and then adapted to the runtime environment to get the work done as efficiently as possible? With the increase in the number of cores on a processor it is becoming more important to take advantage of the available resources; however, coding for the various runtime scenarios can be quite painful. Tools like Parallel LINQ (PLINQ) can let developers provide the declarative instructions for an operation and the tool can determine how best to use the available processor cores. Come see an introduction to PLINQ and see how you can get more work done with less code.

Published in: Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide
  • To set some expectations for the talk, here is what we are going to cover.This is an introduction to PLINQ only. There is no way I could teach you all the ins and outs of PLINQ in an hour’s time, much less even scratch the surface of how cool the Task Parallel Library (TPL) is. This is simply enough information for an introduction to the technology given by an enthusiast.
  • So, what’s going on underthe hood? The extension methods implemented on ParallelQuery and ParallelEnumerable take the place of the standard Enumerable extension methods. These parallel versions of the query operators then call directly into the Task Parallel Library (TPL) which utilizes the lower level Task and TaskManager constructs to parallelize the work across the processors on the box.Note that in the December 07 CTP the traditional ThreadPool was used, but with the June 08 CTP this has been changed over to the TPL. As of the June 08 CTP there was still an environment variable you could set that would force the Thread Pool to be used instead of the TPL.
  • There are three modes of execution in PLINQ: Pipelining, Stop & Go and Inverted Enumeration. Each have their own uses and it’s important to understand how they work so you can select which one you want to use.
  • Pipelining – In this mode of execution (which is also the default) a single thread is tasked with performing the work of the iterator (for example in a foreach bit of code the work within the foreach is handled by the iterator thread) and the other threads actually execute the query. At the iterator finishes with one element and asks for another it simply pulls from a buffer that the other threads are pushing into. This has the benefit that work is being accomplished as the query is executing and since the entire query isn’t executed before processing continues it cuts down some on memory consumption; however, be aware that there is extra overhead in the coordination and synchronization of the of the executing threads.Note that there is an overload on the IParalellEnumerable<T> for GetEnumerator that allows you to specifically turn off pipelining. If you add “false” it will stop the pipelining and perform the Stop & Go processing mode; however, you have to specifically handle this with the Getenumerator and call MoveNext yourself and can’t rely on the foreach construct.NOTE: There was a change in the .NET 4.0 CTP where pipelining no longer reduce MAX DOP by 1, instead I uses all available cores (up to MAX DOP set on the AsParallel call) for it’s work. It still using a thread to process items from the queue as well, but that work will be shared on a CPU that is also using a thread to perform the query.
  • Stop & Go processing is when the thread that started the enumeration will join all other threads created by the TPL in executing the query. The entire query is completed before processing of the query occurs. If you use ToList, ToArray or a Sort then Stop & GO is used.
  • The last execution mode is inverted enumeration. In this mode the query is execute on all threads assigned by the TaskManager. When each executing thread has a set of results for it’s query it will then call a delegate for each of the results. Note that there is no return value from a ForAll call. This is the highest performing execution mode because once the work is split up and partitioned the threads can just execute and no merging or synchronization of the results is required.
  • Time for a Dog and Pony…Demo 2 shows the use of the ForAll syntax to parse a large XML document and add attributes to the nodes found in the query.
  • About now you’re probably thinking, sounds too good to be true. Well, as with everything in our industry, there could be a few gotchas to be on the lookout for.
  • Exceptions. Now you don’t have to worry about one exception, you have to be aware that exceptions can be thrown from multiple threads and come from multiple processors.
  • Luckily the TPL handles this for us. If an exception is thrown by any of the spawned threads then the TPL will tell all the other threads to cancel. It will then roll up all the exceptions it has caught (the first one and any that occurred prior to the other threads stopping) into a single AggregateException. On the AggregateException there is a collection of all the exceptions that were collected called innocently InnerExceptions.
  • Delegates used in your PLINQ queries that are “impure” (meaning they have data side effects) or deal with shared state will most likely not produce the results you were looking for. Pure PLINQ queries will be best used on operations that can be performed completely independently of each other. That’s not to say that things like averages and sums still aren’t supported, they are. Another thing to watch out is any code you may include in your delegates that have thread affinity. Some code (UI code and things like COM) require that some code run on a specific thread in order to operate correctly. PLINQ will not produce the results you are looking for if the functions deal with any objects or code that have thread affinity. PLINQ hides what threads are being used and you have no control over them.
  • In ancient times books were very rare. They were rare because the process to make them was an art. Most people did not know how to read or write, much less how to create a book. This skill was reserved for the clergy or government scribes. The scholars held the respect of many and some considered them masters.Creating books was tedious, painstaking and laborious.
  • In a standard LINQ query the order of the output is the same as the input as long as no specific ordering operation is being performed, such as a sort. In a PLINQ query this is not the case. Having multiple threads means that you can’t guarantee which threads will complete first, so the collected data from the query could be out of order. By default PLINQ does not take order into account; however, if you wish to force the order of a query you can do so with the AsOrdered method.Note that to retain ordering means that you will incur overhead. PLINQ has to put the results back into the original order.In addition, if you have nested queries and some of the query indicates that order is not important, but other parts are order dependent you can use AsUnOrdered method to indicate to the TPL which part of the query needs to retain ordering and which part doesn’t.
  • Amdahl’s Law: The speedup of a program using multiple processors in parallel computing is limited by the sequential fraction of the program. (Wikipedia-'s_law)
  • So what does that actually mean?
  • It means that some portion of your application will still likely be sequential. If a small part of your application can be parallelized and it doesn’t take up that much of the execution time to begin with then you will not see a marked improvement. On the other hand, if your application is almost entirely made up of code that can be easily parallelized, or that small part that can be parallelized takes up the majority of your execution time then you will see a much greater performance improvement.Don’t forget that there is overhead involved with parallelization; this plumbing abstraction isn’t for free. If you have a small collection to run through you may see that parallelizing actually takes longer! PLINQ and the TPL have some intelligence to attempt to pick up on these types of scenarios, but you’ll need to be watchful for them as well.
  • I always find the term “Best Practices” funny, because they change over time. In this case the Parallel Extensions isn’t even baked and out the door, so I can’t give Best Practices. I can give some things that could be considered “Good” Practices to keep you from getting boxed in to a solution.Don’t use delegates in your queries that use shared state or cause data side effects.Don’t assume that just because you are parallelizing it that it will be faster in all cases.Use collections that are easily indexable. This allows for the TPL to be much more efficient in dividing the workload.Be aware of the execution methods and how they differ.
  • Save the Cheerleader, save the world…..DOH!YAGNI! Be aware of what’s going on. Just because you aren’t writing the code to do the work, doesn’t mean you can completely forget that the code will be running in parallel. Make sure that you understand the implications of the parallel work before using PLINQ. PLINQ will not be for everyone and it certainly isn’t a silver bullet. Not all code or applications will even benefit from it. In fact, I believe that very few LOB applications have a need for such a technology, but when you need it, it will be there.
  • Then Johannes Gutenberg invented the printing press which made the job of creating books much simpler. But you were still limited to the fonts and graphics that were carved into the stamps. While the task was much simpler and less skilled labor was involved, the job was still pretty tedious.
  • Over time this creation has given way to the ability for us to simply type up something on the computer and press this button. We get a nice copy of our text, along with images and pictures. All of that stemmed from taking what was essentially an incredibly laborious and difficult task and providing a way to make it easier. So what in the world does this have to do with PLINQ?
  • Threading is hard.Even Jeffrey Richter, someone I’d consider one of the foremost authorities on Threading, agrees that threading is hard. When you throw multiple processors into the mix now the issue becomes even more of a challenge. For many years only the masters have handled advanced threading applications well, while many of us have tried. The problem is, with multithreading you could easily code something that works great in development and test only to have it fall completely apart when you hit production.PLINQ takes something that is very tedious, painstaking, and laborious and turns it into something easy to use.
  • LINQ works by utilizing extension methods off the Ienumberable and Ienumerable<T> classes. The query language we see often in code (featured in the top box above) is just syntactical sugar over extension methods. Both code snippets on this slide produce the same result in the compiled IL.
  • PLINQ simply slides into the mix when AsParallel changes the Ienumerable to an IParallelEnumerable. This then allows for the query methods to utilize the parallelization code. You have to love it when adding plumbing code like this is that easy!
  • Time for a Dog and Pony…Demo 1: Show an example of changing a sequential query into a Parallel query by using AsParallel. Show how it affects the CPU utilization.
  • Using PLINQ you can get more accomplished with less of your own code. But not that I say less of your own code, and not just less code. A lot of the work (and more) that you would have done to handle this yourself is still there, it’s just abstracted into the Parallel Extensions. There is a lot of work going on behind that AsParallel method. Just remember that all this power isn’t for free.There may be situations where doing the work sequentially is less expensive (CPU wise) and time consuming than using PLINQ. Small collections may not overcome the cost associated with the parallel work involved, so keep that in mind.
  • PLINQ - Doing More with Less

    1. 1. @mikewo on Twitter Strategic Data Systems Centerville, OH Doing More With Less An Introduction to Parallel LINQ Presented by:
    2. 2. What is PLINQ? How does it work? What should I watch for? Where can it be used?
    3. 3. I have race conditions and deadlocks….oh, what’s the point! I’ll never get it right. Hard threading is….much have you to learn. Stop whining, he does not.
    4. 4. LINQ utilizes Extension Methods var results = from x in data where check(x) == true select x; IEnumerable<T> var results = data.Where(x => check(x)).Select(x => x);
    5. 5. PLINQ follows the same model var results = data.AsParallel().Where(x => check(x)).Select(x => x);
    6. 6. Demo
    7. 7. PLINQ gets more done with less code!
    8. 8. PLINQ •ParallelQuery.AsParallel •ParallelEnumberable.Select •ParallelEnumberable.Where Task Parallel Library (TPL) TaskManager Proc Proc Proc … 1 2 N
    9. 9. Execution Methods Pipelining Stop & Go Inverted Enumeration Enumerators ToArray() ForAll() Invoked by foreach ToList() Sorts Memory Lower Higher Lower Footprint Depends on Faster than Fastest Performance consumer Pipelining
    10. 10. Pipelining T0 TaskManager T1 T0 TN var results = data.AsParallel().Where(x => check(x)).Select(x => x); IEnumerator iterator = var results = data.AsParallel().Where(x => check(x)).Select(x => x); foreach (var result in results) ((IParallelEnumerable<int>)results).GetEnumerator(false); while (iterator.MoveNext()) { { someOperation(result); } someOperation(iterator.Current); }
    11. 11. Execution Methods Pipelining Stop & Go Inverted Enumeration Enumerators ToArray() ForAll() Invoked by foreach ToList() Sorts Memory Lower Higher Lower Footprint Depends on Faster than Fastest Performance consumer Pipelining
    12. 12. All threads execute Stop & GO the query! T0 TaskManager T1 T0 … TN var results = data.AsParallel().Where(x => check(x)).Select(x => x); Will cause Stop & GO var q = results.ToList();
    13. 13. Execution Methods Pipelining Stop & Go Inverted Enumeration Enumerators ToArray() ForAll() Invoked by foreach ToList() Sorts Memory Lower Higher Lower Footprint Depends on Faster than Fastest Performance consumer Pipelining
    14. 14. Inverted Enumeration T0 T0 SomeOp(x) TaskManager T1 SomeOp(x) T1 … TN TN SomeOp(x) var results = data.AsParallel().Where(x => check(x)).Select(x => x); results.ForAll(result => SomeOp(result));
    15. 15. Execution Methods Pipelining Stop & Go Inverted Enumeration Enumerators ToArray() ForAll() Invoked by foreach ToList() Sorts Memory Lower Higher Lower Footprint Depends on Faster than Fastest Performance consumer Pipelining
    16. 16. Demo
    17. 17. Exceptions!
    18. 18. PLINQ 4 TPL 3 TPL wraps all exceptions in an TaskManager AggregateException 2 Proc Proc 1 2 1
    19. 19. Impure Functions, Shared State and Thread Affinity
    20. 20. Ordering var results = from x in data.AsParallel().AsOrdered() where check(x) == true select x; … var results = data.AsParallel().AsOrdered().Where(x => check(x)).Select(x => x);
    21. 21. The speedup of a program using multiple processors in parallel computing is limited by the sequential fraction of the program.
    22. 22. What the heck does that mean?
    23. 23. Application A will see some performance improvement Application A Application B Application B will see greater improvement
    24. 24. Best Practices with PLINQ
    25. 25. Write the PLINQ query, save the world!
    26. 26. PLINQ provides an abstraction layer via extension methods which introduces parallelism into your LINQ queries against in-memory objects and XML with few changes to your existing code.
    27. 27. Strategic Data Systems Centerville, OH @mikewo on Twitter
    28. 28. Resources Presentation Materials MSDN Parallel & Concurrency Development Center Parallel Extensions Download (June 08 CTP) Parallel Extensions Team Blog .NET 4.0/Visual Studio 2010 CTP More Resources can be found in the presentation materials.