4

*Please note that I am just testing to understand this.

I am trying to use all the cores of my computer with the Parallel.For() method. This is working just fine, but when I am trying the same method with a normal for loop it is going much faster. The parallel method is taking 16 sec and the normal method is only taking 6 sec.

I hope you can tell me what I am doing wrong here.

Updated code

        DateTime parallelStart = new DateTime();
        DateTime parallelFinish = new DateTime();
        DateTime singeStart = new DateTime();
        DateTime singeFinish = new DateTime();
        parallelStart = DateTime.Now;
        int inputData = 0;

        Parallel.For(0, 1000000000, i =>
        {
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
        });

        parallelFinish = DateTime.Now;
        singeStart = DateTime.Now;

        for (int i = 0; i < 1000000000; i++)
        {
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
            inputData = inputData++;
        }

        singeFinish = DateTime.Now;
        MessageBox.Show("Parallel execution time: " + (parallelFinish - parallelStart).Seconds + "\n" +
                        "Singe execution time: " + (singeFinish - singeStart).Seconds);

First code:

DateTime parallelStart = new DateTime();
DateTime parallelFinish = new DateTime();
DateTime singeStart = new DateTime();
DateTime singeFinish = new DateTime();
parallelStart = DateTime.Now;

Parallel.For(0, 2000000000, i =>
{
    var inputData = 0;
});

parallelFinish = DateTime.Now;
singeStart = DateTime.Now;

for (int i = 0; i < 2000000000; i++)
{
    var inputData = 0;
}

singeFinish = DateTime.Now;
MessageBox.Show("Parallel execution time: " + (parallelFinish - parallelStart).Seconds + "\n" + "Singe execution time: " + (singeFinish - singeStart).Seconds);
Patrick Karcher
  • 22,995
  • 5
  • 52
  • 66
7heViking
  • 7,137
  • 11
  • 50
  • 94
  • 5
    I would assume some sort of optimization might be removing your loop, since it doesn't do anything. And that's not the proper use of DateTime.Now. There's just a lot of mistakes. – Joe Jan 11 '12 at 14:38
  • possible duplicate of [Parallel.ForEach Slower than ForEach](http://stackoverflow.com/questions/6036120/parallel-foreach-slower-than-foreach) – Brian Jan 11 '12 at 14:43
  • I tried to add some work to it at now it takes the same tome each... So you are all right (ofcurse). It just need someting to work with :) – 7heViking Jan 11 '12 at 14:51
  • 1
    Joe is right; use StopWatch for performance analysis. DateTime.Now does not have a good enough level of precision. – Eric Lippert Jan 11 '12 at 14:52
  • Issues you should not do not directly related to your performance problem. `inputData = inputData++;` should not be done as the `++` already updates the vale. Also (and the more important issue) you are likely making a mess with the value of the variable due to all of the threads reading and writing to the same variable without any kind of synchronization. – Scott Chamberlain Jan 11 '12 at 16:31
  • I think `DateTime.UtcNow` is fine for this kind of benchmark. If it's accuracy isn't enough, the benchmark simply doesn't run long enough. Most of my benchmarking simply uses the runtime LinqPad displays. – CodesInChaos Jan 11 '12 at 19:15
  • What is the output when you change to TotalSeconds instead of Seconds? – Emond Jan 11 '12 at 21:29
  • @Erno In this case it would be the same since it take less than 1 minute. – 7heViking Jan 12 '12 at 07:24
  • @FireFly3000 - If the test takes less than 1 minute I do not trust it. – Emond Jan 12 '12 at 07:58

8 Answers8

14

Well, what's faster: doing no work yourself, or hiring ten guys to each do one tenth of no work?

What you're doing wrong is that your example is completely artificial. The jitter is fully aware that assigning zero to the same local that is unused over and over again is pointless, so it is probably eliminating the work. Even if it is not, that work is trivial. The work of setting up all the threads is larger than just doing the work yourself.

Do a real test -- do some raytracing or generate a fractal or something in there. Do something realistic that will take minutes or hours on one thread. You cannot reasonably expect to see a savings on a job that is too easy.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
10

This is probably because the overhead of creating the threads and the contention between them takes more time than the operation itself. Try putting something more complex inside that loop and I'd bet your results will change.

Eric Petroelje
  • 59,820
  • 9
  • 127
  • 177
5

Setting the value of an existing value type on the stack is incredibly, profoundly trivial. The cost of creating and keeping track of the threads would be large compared to this. Adding any complexity to your operation would drastically change your results. Even creating and setting a string variable equal to the appending of two strings would massively increase the work done. Try something more complicated (almost anything will do!) and you'll see the value of the parallel library increase.

Try this:

Parallel.For(0, 2000000000, i =>
{
    var string inputData = "ninjas " + "like " + "taco";
});

Believe it or not, we have added a lot more processing here. (There's multiple strings being created and tossed in the background) This will change your results. And then when you consider that we do vastly more complicated things in these loops, like:

  • data-access
  • disk IO
  • complicated math
  • filtering / ordering / projecting of lists, etc.

, the results will be dramatic in favor of parallelism.

Patrick Karcher
  • 22,995
  • 5
  • 52
  • 66
2

Multithreading has overhead. For operations that take a long time compared to the time taken to set up the thread, great.

But in your case, you have a very simple loop body, and the overhead of creating the threads to do the work is outweighing the benefit of doing it in parallel.

crashmstr
  • 28,043
  • 9
  • 61
  • 79
2

Parallelization of code involves a fairly large overhead, and you are using it to perform trivial operations. That's why your code runs very slowly. You will only see an improvement once you flesh-in your code, and your code does plenty of CPU-intensive operations.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
1

As most books on parallel programming will mention, "always measure performance." Don't assume that going parallel is faster than using a straight forward loop, because parallelism includes overhead.

You really need to consider how much data needs to be processed, the level of concurrency and and how much sharing of volatile data needs to occur (how much locking is required).

Giovanni Galbo
  • 12,963
  • 13
  • 59
  • 78
0

Use TotalSeconds instead of Seconds when measuring time.

Emond
  • 50,210
  • 11
  • 84
  • 115
  • 1
    A valid correction, but he should be using a [`StopWatch`](http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx). – Brian Jan 11 '12 at 14:52
  • In that case he'd still have to use TotalSeconds or TotalMilliseconds – Emond Jan 11 '12 at 14:57
0

I suspect, without empirical evidence of my own, that the overhead of queuing the additional thread outweights the benefit. What you are asking for is 2bn pieces of work to be executed concurrently, which without checking I suspect Parallel queues with the ThreadPool.

Myles McDonnell
  • 12,943
  • 17
  • 66
  • 116