How can we be sure that the number of iterations of commands in "Parallel.ForEach" matches the number of iterations that were set for it?

Reza Jaferi 331 Reputation points
2022-12-21T18:44:08.537+00:00

Hi guys,
In my article, I want to compare the speed of sequential processing to parallel processing in various conditions, so in one example, I want to convert the following sequential command to parallel, but after converting it to parallel, it seems that the loop is not repeated for the specified number of times. It may be necessary to use Concurrent Collection or a lock statement, but this usually reduces the speed of parallel processing compared to sequential processing.

So I want an example (preferably a simple example) for my article that compares for and Parallel.ForEach. This example should have the following features:
• The execution of Parallel.ForEach block codes should be faster than the execution of ForEach block codes.
• Using a flag, a variable, or other mechanisms, ensure that the Parallel.ForEach loop is executed the predefined number of times.
Something like the following code (Parallel.For vs for loop):

using System;  
using System.Diagnostics;  
using System.Threading;  
using System.Threading.Tasks;  
  
namespace ConsoleApp  
{  
    class Program  
    {  
        const int LOOP_COUNT = 900000;  
        static long Calcute()  
        {  
            int Total = 0;  
            for (int i = 0; i < 10000; i++) // add more iteration to make parallel faster than sequential  
            {  
                Total += 20 * i;  
            }  
            return Total;  
        }  
  
        static void SequentialCalculate()  
        {  
            var Counter = 0;  
            long Total = 0;  
            Stopwatch SW = new Stopwatch();  
            SW.Start();  
            for (int i = 0; i < LOOP_COUNT; i++)  
            {  
                Total = Calcute();  
                ++Counter;  
            }  
            SW.Stop();  
            Console.WriteLine("Sequential total seconds: " + SW.Elapsed.TotalSeconds + ", Counter: " + Counter + ", Total: " + Total);  
        }  
  
        static void ParallelCalculate()  
        {  
            var Counter = 0;  
            long Total = 0;  
            Stopwatch SW = new Stopwatch();  
            SW.Start();  
            Parallel.For(0, LOOP_COUNT, i =>  
            {  
                Total = Calcute();  
                Interlocked.Add(ref Counter, 1);  
            });  
            SW.Stop();  
            Console.WriteLine("Parallel total seconds: " + SW.Elapsed.TotalSeconds + ", Counter: " + Counter + ", Total: " + Total);  
        }  
  
        static void Main(string[] args)  
        {  
            Console.ReadKey();  
            SequentialCalculate();  
            ParallelCalculate();  
            Console.ReadKey();  
        }  
    }  
}  

Result (by Intel Core i5-2430M, no debug mode):
272916-result.png

I tried this too but didn't get a proper result:

using System;  
using System.Collections.Concurrent;  
using System.Diagnostics;  
using System.Threading.Tasks;  
  
namespace ConsoleApp  
{  
    class Program  
    {  
        const long Count = 50000;  
        static long[] Numbers = new long[Count];  
        static long Calculate()  
        {  
            long Total = 0;  
            foreach (long Num in Numbers)  
            {  
                Total += Num;  
            }  
            return Total;  
        }  
  
        static void SequentialCalculate()  
        {  
            long Total = 0;  
            Stopwatch SW = new Stopwatch();  
            SW.Start();  
            foreach (long N in Numbers)  
            {  
                Total += N * Calculate();  
            }  
            SW.Stop();  
            Console.WriteLine("Sequential total seconds: " + SW.Elapsed.TotalSeconds + ", Total: " + Total);  
        }  
        static void ParallelCalculate()  
        {  
            long Total = 0;  
            Stopwatch SW = new Stopwatch();  
            SW.Start();  
            Parallel.ForEach((Partitioner.Create(0, Numbers.Length)), range =>  
            {  
                for (long N = range.Item1; N < range.Item2; N++)  
                {  
                    Total += N * Calculate();  
                }  
            });  
            SW.Stop();  
            Console.WriteLine("Parallel total seconds: " + SW.Elapsed.TotalSeconds + ", Total: " + Total);  
        }  
        static void Main(string[] args)  
        {  
            for (long i = 0; i < Count; i++)  
            {  
                Numbers[i] = i;  
            }  
            Console.WriteLine("Ready to calculate");  
            Console.ReadKey();  
            SequentialCalculate();  
            ParallelCalculate();  
            Console.ReadKey();  
        }  
    }  
}  

Result (by Intel Core i5-2430M, no debug mode):
273481-result.png

I use the following tools:

• .NET Framework 4.5

• Console app

• Windows 7

Thank you for your time.

.NET CLI
.NET CLI
A cross-platform toolchain for developing, building, running, and publishing .NET applications.
337 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,008 questions
{count} votes

Accepted answer
  1. Jack J Jun 24,516 Reputation points Microsoft Vendor
    2022-12-26T08:23:02.203+00:00

    @Reza Jaferi , Welcome to Microsoft Q&A, based on my test, I reproduced your problem.
    When we want to use Parallel.Foreach method to, it actually used many threads to do a += operation at the same time. And it could not be used in multi-thread.

    Please refer to the answer to know more about it. We often used Interlocked.Add to solve the similar problem.

    Updated code example to use Interlocked.Add:

    Parallel.ForEach((Partitioner.Create(0, Numbers.Length)), range =>  
                 {  
                     for (long N = range.Item1; N < range.Item2; N++)  
                     {  
                          Interlocked.Add(ref Total, N * Calculate());  
                     }  
                 });  
    

    Result (by Intel Core i5-2430M, no debug mode):

    274233-image.png
    Hope my explanation could help you.

    Best Regards,
    Jack


    If the answer is the right solution, please click "Accept Answer" and upvote it.If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    2 people found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.