Parallel.ForEach Slower than ForEach

Mike Diaz picture Mike Diaz · May 17, 2011 · Viewed 23.2k times · Source

Here is the code:

using (var context = new AventureWorksDataContext())
{
    IEnumerable<Customer> _customerQuery = from c in context.Customers
                                           where c.FirstName.StartsWith("A")
                                           select c;

    var watch = new Stopwatch();
    watch.Start();

    var result = Parallel.ForEach(_customerQuery, c => Console.WriteLine(c.FirstName));

    watch.Stop();
    Debug.WriteLine(watch.ElapsedMilliseconds);

    watch = new Stopwatch();
    watch.Start();

    foreach (var customer in _customerQuery)
    {
        Console.WriteLine(customer.FirstName);
    }

    watch.Stop();
    Debug.WriteLine(watch.ElapsedMilliseconds);
}

The problem is, Parallel.ForEach takes about 400ms vs a regular foreach, which takes about 40ms. What exactly am I doing wrong and why doesn't this work as I expect it to?

Answer

Eric Lippert picture Eric Lippert · May 17, 2011

Suppose you have a task to perform. Let's say you're a math teacher and you have twenty papers to grade. It takes you two minutes to grade a paper, so it's going to take you about forty minutes.

Now let's suppose that you decide to hire some assistants to help you grade papers. It takes you an hour to locate four assistants. You each take four papers and you are all done in eight minutes. You've traded 40 minutes of work for 68 total minutes of work including the extra hour to find the assistants, so this isn't a savings. The overhead of finding the assistants is larger than the cost of doing the work yourself.

Now suppose you have twenty thousand papers to grade, so it is going to take you about 40000 minutes. Now if you spend an hour finding assistants, that's a win. You each take 4000 papers and are done in a total of 8060 minutes instead of 40000 minutes, a savings of almost a factor of 5. The overhead of finding the assistants is basically irrelevant.

Parallelization is not free. The cost of splitting up work amongst different threads needs to be tiny compared to the amount of work done per thread.

Further reading:

https://en.wikipedia.org/wiki/Amdahl%27s_law

https://en.wikipedia.org/wiki/Gustafson%27s_law