Luka
Luka

Reputation: 4211

Why C# Parallel.Invoke is slow?

I am doing this:

    private static void Main(string[] args)
    {
        var dict1 = new Dictionary<int, string>();
        var dict2 = new Dictionary<int, string>();
        DateTime t1 = DateTime.Now;
        for (int i = 1; i < 1000000; i++)
        {
            Parallel.Invoke(
                  () => dict1.Add(i, "Test" + i), 
                  () => dict2.Add(i, "Test" + i) );
        }
        TimeSpan t2 = DateTime.Now.Subtract(t1);

        Console.WriteLine(t2.TotalMilliseconds);

        Console.ReadLine();
    }

So doing a for loop 1 million time and adding items to two different dictionaries. The problem is that it takes 11 secs which is more than 5 time the normal sequential method (without tasks/threads) which takes only 2 sec. Don't know why.

Upvotes: 3

Views: 5248

Answers (5)

scottm
scottm

Reputation: 28701

If what you want to do is add 100000 items to two different dictionaries, you should break the work load between tasks, not the method. This method does the same as your code, but on my machine is much faster than your implementation:

var dict1 = new ConcurrentDictionary<int, string>();
    var dict2 = new ConcurrentDictionary<int, string>();

    Parallel.Invoke(() =>
    {
        for(int i = 0; i < 500000; i++)
        {
            dict1[i] = "Test" +i;
            dict2[i] ="Test" +i;
        }
    },
    () =>
    {
        for(int i = 500000; i < 1000000; i++)
        {
            dict1[i] ="Test" +i;
            dict2[i] = "Test" +i;
        }
    });

Upvotes: 0

Wormbo
Wormbo

Reputation: 4992

Rewrite that to something like:

Parallel.Invoke(
    () =>
    {
        for (int i = 0; i < 1000000; i++)
        {
            dict1.Add(i, "Test" + i);
        }
    },
    () =>
    {
        for (int i = 0; i < 1000000; i++)
        {
            dict2.Add(i, "Test" + i);
        }
    }
);

That should be a lot faster because the two threads are initialized exactly once and then run to completion. In your version you are calling each lambda expression 1000000 times, each time waiting for both to finish before continuing.

Parallel.Invoke is really meant to be used for longer running operations. Otherwise the overhead of setting up the parallel tasks and waiting for them all to finish just kills any performance gained by running the tasks in parallel.

Upvotes: 5

Austin Salonen
Austin Salonen

Reputation: 50225

Like others have said or implied, parallel code is not always faster due to the overhead of parallelization.

That being said, your code is adding an item to 2 dictionaries in parallel 1M times while you should be adding 1M items to 2 dictionaries in parallel. The difference is subtle but the end result is code that is ~10% faster (on my machine) than your sequential case.

Parallel.Invoke(() => FillDictionary(dict1, 1000000), () => FillDictionary(dict2, 1000000));

...

private static void FillDictionary(Dictionary<int, string> toFill, int itemCount)
{
    for(int i = 0 ; i < itemCount; i++)
        toFill.Add(i, "test" + i);
}

Upvotes: 10

Greg
Greg

Reputation: 23463

Parallel.Invoke means "perform all these tasks and wait until they are done." In this case, you are only ever doing two tasks in parallel. Thus the overhead of parallel invocation is greater than the potential gains from concurrency.

Upvotes: 1

Andrey
Andrey

Reputation: 60065

There is certain overhead using parallel invocation and benefit of distributing work across multiple cores/CPUs. In this case overhead is bigger than the actual benefit from distribution of useful work, so that's why you see significant difference. Try using more heavy operations and you will see the difference.

Upvotes: 6

Related Questions