Course contentsShow
← C#
Lesson 659 of 1,9086. Asynchronous Programming and ConcurrencyFree lesson

Parallel Loop Partitioning

Control how PLINQ and Parallel loops partition work across available processor cores.

In this lesson, you will learn how to influence how Parallel.For and Parallel.ForEach distribute chunks of work across your CPU cores to achieve optimal performance.

The Concept of Partitioning

When you run a Parallel.For loop, the .NET runtime doesn't simply give one iteration to each thread at a time. Doing so would cause massive overhead due to constant communication between threads. Instead, the runtime uses partitioning: it divides your data collection into smaller chunks (or "partitions"), and assigns these chunks to different processor cores.

By default, the Parallel class uses heuristics to choose the best partitioning strategy based on the size of your data and the complexity of the work. However, you can manually control this process using Partitioner.Create.

Why Partition Manually?

If your loop body performs very simple operations (like adding two integers), the overhead of managing threads can actually make the parallel loop slower than a standard for loop. By using Range Partitioning, you can group thousands of iterations into a single chunk, significantly reducing the overhead.

Practical Example

If you have a collection of 1,000,000 items, you can use Partitioner.Create to tell the system exactly how to break that work up:

using System.Collections.Concurrent;

var data = Enumerable.Range(0, 1000000).ToList();

// Manually partition the data into chunks of 10,000 items
var partitioner = Partitioner.Create(0, data.Count, 10000);

Parallel.ForEach(partitioner, range => 
{
    for (int i = range.Item1; i < range.Item2; i++)
    {
        // Process your data here
        data[i] = data[i] * 2;
    }
});

By setting the range size (in this case, 10,000), you balance the workload. Larger chunks reduce synchronization overhead between cores, while smaller chunks ensure better load balancing if some iterations take significantly longer to complete than others. If your work items are highly variable in duration, smaller partitions are safer; if they are uniform, larger partitions are faster.

Key Takeaway: Partitioning allows you to strike a balance between thread management overhead and effective load distribution by explicitly defining how data chunks are assigned to processor cores.