csharp

Boost Performance with AsParallel in C# LINQ

Introduction

When working with large in-memory collections in C#, LINQ queries can become slow, especially when filtering or transforming thousands of records. This is where AsParallel() comes in. It enables P.arallel LINQ (PLINQ), which uses multiple CPU cores to improve performance significantly. In this blog, we’ll explore AsParallel in C# with a real-world example involving 2 lakh (200,000) records

What is AsParallel in C#?

AsParallel() is a method that transforms a LINQ query into a parallel query, distributing the work across multiple threads. This is ideal for CPU-bound operations like large-scale data filtering, calculations, and transformations.

Under the hood, AsParallel() is part of the PLINQ (Parallel LINQ) library, which intelligently divides the data source into segments and processes them in parallel using available CPU cores. You can chain AsParallel() with your existing LINQ queries without changing the logic—making it a powerful and non-intrusive optimization.

PLINQ can automatically balance the load and choose the optimal number of threads based on the system’s capabilities. However, it’s not always a magic fix—developers should measure performance to determine if it genuinely improves speed for a given scenario.

as-parallel

Real-World Example: Filtering 2 Lakh Products

Suppose you have a product list with 200,000 records. You want to find all expensive electronics priced over 5000. Here’s how AsParallel() can help:

public class Product
{
    public int Id { get; set; }
    public string Category { get; set; }
    public double Price { get; set; }
}

List<Product> products = new List<Product>();
for (int i = 0; i < 200000; i++)
{
    products.Add(new Product
    {
        Id = i,
        Category = i % 2 == 0 ? "Electronics" : "Clothing",
        Price = i * 0.5
    });
}

var expensiveElectronics = products
    .AsParallel()
    .Where(p => p.Category == "Electronics" && p.Price > 5000)
    .ToList();

Console.WriteLine($"Total expensive electronics: {expensiveElectronics.Count}");

This query runs faster on machines with multiple cores, because it divides the workload across threads.

Benefits of Using AsParallel

  • Faster processing for large data collections
  • Efficient CPU usage with multi-core support
  • Easy to integrate with existing LINQ queries

Disadvantages

  • Order not guaranteed (use .AsOrdered() if needed)
  • Not suitable for small datasets (thread overhead)
  • Debugging is harder due to concurrency

How to Maintain the Original Order in Parallel LINQ?

By default, AsParallel() does not preserve the original sequence of the elements. If you need results in the original order, use .AsOrdered():

var orderedResults = products
    .AsParallel()
    .AsOrdered()
    .Where(p => p.Price > 5000)
    .ToList();

This ensures your output maintains the same order as the source collection, although it may slightly reduce performance.

Difference Between OrderBy and AsOrdered Method in C#

  • OrderBy sorts the data based on a specified key (e.g., Price, Name).
  • AsOrdered ensures that results maintain the original input sequence after processing in parallel.
// Maintains original order
products.AsParallel().AsOrdered()

// Sorts by price
products.AsParallel().OrderBy(p => p.Price)

Use AsOrdered() when you care about input order. Use OrderBy() when you want to sort data by a custom key.

Maximum Degree of Parallelism and Cancellation Token in PLINQ

You can control the number of parallel tasks using WithDegreeOfParallelism() and cancel the query using WithCancellation():

CancellationTokenSource cts = new CancellationTokenSource();

var results = products
    .AsParallel()
    .WithDegreeOfParallelism(4)
    .WithCancellation(cts.Token)
    .Where(p => p.Price > 5000)
    .ToList();
  • WithDegreeOfParallelism(int) limits the number of concurrent tasks.
  • WithCancellation(token) allows you to abort the query when needed.

Doing Aggregates in Parallel LINQ

PLINQ supports aggregation operations like Sum, Count, and Average:

var totalExpensivePrice = products
    .AsParallel()
    .Where(p => p.Price > 5000)
    .Sum(p => p.Price);

Console.WriteLine($"Total Price: {totalExpensivePrice}");

Use aggregates carefully in parallel queries, especially if you’re using custom reducers or complex transformations.

Conclusion

If you’re dealing with large in-memory datasets in C#, using AsParallel can drastically improve query performance with minimal code changes. Just be sure to use it wisely, benchmark the results, and avoid it for small collections or I/O-bound operations.

Implementing AsParallel in C# is a quick win for performance optimization in .NET applications!

2 Comments

  1. Surajit Sadhukhan

    This is excellent Satya. Could put some lights on how to use AsParallel().WithDegreeOfParallelism() and explain how its worked. What the tradeoff of using WithDegreeOfParallelism

Leave a Reply

Your email address will not be published. Required fields are marked *