PLINQ (Parallel LINQ) is a parallelized implementation of LINQ (Language Integrated Query) that enables queries to execute in parallel, taking advantage of multi-core processors to improve performance.
Key Features of PLINQ
- Automatic Parallelization: PLINQ divides the data source into partitions and processes them on multiple threads automatically.
- Declarative Syntax: The syntax remains similar to LINQ, making it easier to use for developers familiar with LINQ.
- Scalability: PLINQ leverages all available processors for improved performance on large datasets.
- Customizable Execution: You can control the degree of parallelism and execution behavior.
Basic Example: Parallelizing a Query
using System;
using System.Linq;
class Program
{
static void Main()
{
var numbers = Enumerable.Range(1, 100);
// Parallel LINQ query
var evenNumbers = numbers
.AsParallel()
.Where(n => n % 2 == 0)
.ToList();
Console.WriteLine("Even numbers:");
evenNumbers.ForEach(Console.WriteLine);
}
}
Example: Measuring Performance
using System;
using System.Linq;
using System.Diagnostics;
class Program
{
static void Main()
{
var numbers = Enumerable.Range(1, 10_000_000).ToArray();
var stopwatch = new Stopwatch();
// Sequential processing
stopwatch.Start();
var sequentialSum = numbers.Where(n => n % 2 == 0).Sum();
stopwatch.Stop();
Console.WriteLine($"Sequential Sum: {sequentialSum}, Time: {stopwatch.ElapsedMilliseconds}ms");
// Parallel processing
stopwatch.Restart();
var parallelSum = numbers.AsParallel().Where(n => n % 2 == 0).Sum();
stopwatch.Stop();
Console.WriteLine($"Parallel Sum: {parallelSum}, Time: {stopwatch.ElapsedMilliseconds}ms");
}
}
Controlling Degree of Parallelism
using System;
using System.Linq;
class Program
{
static void Main()
{
var numbers = Enumerable.Range(1, 100);
var results = numbers
.AsParallel()
.WithDegreeOfParallelism(2) // Use 2 processors
.Where(n => n % 2 == 0)
.ToList();
results.ForEach(Console.WriteLine);
}
}
Handling Exceptions in PLINQ
using System;
using System.Linq;
class Program
{
static void Main()
{
var numbers = new[] { 1, 2, 3, 0, 5 };
try
{
var results = numbers.AsParallel()
.Select(n => 100 / n)
.ToList();
}
catch (AggregateException ex)
{
foreach (var innerException in ex.InnerExceptions)
{
Console.WriteLine(innerException.Message);
}
}
}
}
Preserving Order with PLINQ
using System;
using System.Linq;
class Program
{
static void Main()
{
var numbers = Enumerable.Range(1, 10);
var orderedResults = numbers
.AsParallel()
.AsOrdered()
.Where(n => n % 2 == 0)
.ToList();
Console.WriteLine("Ordered Results:");
orderedResults.ForEach(Console.WriteLine);
}
}
When to Use PLINQ?
- Computationally Intensive Tasks: PLINQ is suitable for tasks like image processing, mathematical computations, or large dataset analysis.
- Large Datasets: It works best when processing large collections that can benefit from parallel execution.
- Multi-Core Systems: PLINQ leverages the available CPU cores for parallel execution.
Limitations of PLINQ
- For small datasets or simple queries, the overhead of parallelization may outweigh the benefits.
- Avoid using PLINQ when queries modify shared state, as this can lead to race conditions.
- PLINQ runs on multiple threads, so ensure thread safety in operations.
Best Practices
- Measure performance using tools like Stopwatch to ensure PLINQ benefits your use case.
- Use AsOrdered only when necessary, as it introduces additional overhead.
- Handle exceptions using AggregateException.
PLINQ is a powerful tool for parallel processing, offering significant performance improvements when used correctly. It simplifies writing parallel code without needing to manage threads explicitly.