Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AsParallel() executing sequentially

I have the following PLINQ query:

// Let's get a few customers
List<Customer> customers = CustomerRepository.GetSomeCustomers();

// Let's get all of the items for all of these customers
List<CustomerItem> items = customers
    .AsParallel()
    .SelectMany(x => ItemRepository.GetItemsByCustomer(x))
    .ToList();

I would expect GetItemsByCustomer() to be executed in parallel for each customer, but it runs sequentially.

I have tried to force parallelism but still without luck:

List<CustomerItem> items = customers
    .AsParallel()
    .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
    .SelectMany(x => ItemRepository.GetItemsByCustomer(x))
    .ToList();

The method signature:

private IEnumerable<Item> GetItemsByCustomer(Customer customer)
{
    // Get all items for a customer...
}

According to this article, PLINQ can certainly take the sequential route if it deems fit, but forcing parallelism should still override this.

Note: This above example is purely illustrative - assume customers to be a small list and GetItemsByCustomer to be an expensive method.

like image 345
Dave New Avatar asked Oct 06 '14 06:10

Dave New


1 Answers

There is nothing wrong with AsParallel(). It will run as parallel if possible, and there is no sequential dependency in your LINQ expression, so there is nothing to force it to run sequentially.

A couple of reasons why your code doesn't run in parallel could be:

  1. Your box/vm has a single CPU or you have a .NET setting to limit the parallelism to one CPU. You can Simulate that with this code:

          var customers = new List<Customer>() { new Customer() {Name = "Mick", Surname = "Jagger"}, new Customer() {Name = "George", Surname = "Clooney"},new Customer() {Name = "Kirk", Surname = "DOuglas"}};
    
          var items = customers
            .AsParallel()
            .SelectMany(x =>
            {
                 Console.WriteLine("Requesting: " + x.Name + " - " + DateTime.Now);
                 Thread.Sleep(3000);
                 return new List<CustomerItem>();
    
            })
            .WithDegreeOfParallelism(1)
            .ToList();
    

    Even if you force paralelism with WithExecutionMode(ParallelExecutionMode.ForceParallelism) on a single core/CPU box or when the degree of parallelism is 1, your setting will not have effect, since true parallelism is not possible.

  2. There is some thread locking on shared resources happening in your repository. You can simulate thread locking with the following code:

        var customers = new List<Customer>() { new Customer() {Name = "Mick", Surname = "Jagger"}, new Customer() {Name = "George", Surname = "Clooney"},new Customer() {Name = "Kirk", Surname = "DOuglas"}};
    
        var locker = new object();
    
        // Let's get all of the items for all of these customers
        var items = customers
            .AsParallel()
            .SelectMany(x =>
            {
                lock (locker)
                {
                    Console.WriteLine("Requesting: " + x.Name + " - " + DateTime.Now);
                    Thread.Sleep(3000);
                    return new List<CustomerItem>();
                }
    
            })
            .ToList();
    
  3. There is some Database setting that is forcing the queries/reads to be sequential under certain circumstances, and that could give you an impression that your C# code is not running in parallel, while it actually is.

like image 96
Faris Zacina Avatar answered Nov 14 '22 11:11

Faris Zacina