Optimizing Parallel.For Performance

I replaced the for loop in my code with Parallel.For. The improvement in productivity is amazing (1/3 of the working time). I tried to account for shared resources by using an array to collect the result codes. Then I process the array from the Parallel.For side. Is this the most efficient way, or will it block even if no iteration can ever use the same loop index? Will CompareExchange perform much better?

int[] pageResults = new int[arrCounter];
Parallel.For(0, arrCounter, i =>
{
   AlertToQueueInput input = new AlertToQueueInput();
   input.Message = Messages[i];

   pageResults[i] = scCommunication.AlertToQueue(input).ReturnCode;
});

foreach (int r in pageResults)
{
    if (r != 0 && outputPC.ReturnCode == 0) outputPC.ReturnCode = r;
}

      

+3


source to share


3 answers


It depends if you have any (valuable) side effects in the main loop.

When value outputPC.ReturnCode

is the only result, you can use PLINQ:

outputPC.ReturnCode = Messages
    .AsParallel()
    .Select(msg =>    
    {
       AlertToQueueInput input = new AlertToQueueInput();
       input.Message = msg;        
       return scCommunication.AlertToQueue(input).ReturnCode;
    })
    .FirstOrDefault(r => r != 0);

      



This assumes scCommunication.AlertToQueue () is thread safe and you don't want to call it on the rest of the elements after the first error.

Note that FirstOrDefault () in PLinq is effective in Framework 4.5 and later .

+6


source


You can replace:

foreach (int r in pageResults)
{
    if (r != 0 && outputPC.ReturnCode == 0) outputPC.ReturnCode = r;
}

      

from:



foreach (int r in pageResults)
{
    if (r != 0)
    {
        outputPC.ReturnCode = r;
        break;
    }
}

      

This will stop the loop on the first failure.

+3


source


I like David Arno's solution, but as I see you can improve speed by placing a check inside a parallel loop and breaking right out of it. Anyway, you will lose the main code if any of the iterations fail, so there is no need for further iterations.

Something like that:

Parallel.For(0, arrCounter, (i, loopState) =>
{
   AlertToQueueInput input = new AlertToQueueInput();
   input.Message = Messages[i];
   var code = scCommunication.AlertToQueue(input).ReturnCode;
    if (code != 0)
    {
        outputPC.ReturnCode = code ;
         loopState.Break();
    }

});

      

Update 1:

If you need to store the result of all iterations, you can do something like this:

int[] pageResults = new int[arrCounter];
Parallel.For(0, arrCounter, (i, loopState) =>
    {
       AlertToQueueInput input = new AlertToQueueInput();
       input.Message = Messages[i];
       var code = scCommunication.AlertToQueue(input).ReturnCode;
       pageResults[i] = code ;
        if (code != 0 && outputPC.ReturnCode == 0)
            outputPC.ReturnCode = code ;    
    });

      

It will save you from a cycle foreach

that is an improvement, albeit a small one.

UPD 2:

just found this post and I think the usual parallel is a good solution too. But it is your challenge to decide if it suits your task.

+3


source







All Articles