1

This is just an extension of the following question I asked:

Why can't I reach 100% CPU utilization with my parallel tasks code?

 private static int SumParallel()
        {
            var intList = Enumerable.Range(1, 1000_000_000);
            int count = intList.Count();
            int threads = 6;
            List<Task<int>> l = new List<Task<int>>(threads);
            for(int i = 1; i <= threads; i++)
            {
                int skip = ((i - 1) * count) / threads;
                int take = count / threads;
                l.Add(GetSum(intList, skip, take));
            }
            Task.WaitAll(l.ToArray());
            return l.Sum(t => t.Result);
        }

private static Task<int> GetSum(IEnumerable<int> list, int skip, int take)
        {
            return Task.Run(() =>
             {
                 int temp = 0;
                 foreach(int n in list.Skip(skip).Take(take))
                 {
                     if (n % 2 == 0)
                         temp -= n;
                     else
                     {
                         temp += n;
                     }
                 }

                 Console.WriteLine(temp + " " + Task.CurrentId);
                 return temp;
             });
        }

If I modify the number of tasks working in parallel, I get a different sum.

Why is this so?

teenup
  • 7,459
  • 13
  • 63
  • 122
  • `WaitAll` is blocking call – Pavel Anikhouski May 30 '20 at 09:13
  • This is needed, I have to wait for all of them to finish, only then I have the result. – teenup May 30 '20 at 09:15
  • How many cores does you micro have? You are creating processes and depending on how the operating system is swapping processes will determine the CPU utilization. – jdweng May 30 '20 at 09:23
  • 1
    it can be simplified to `return ParallelEnumerable.Range(1, 1000_000_000).Sum(n => n % 2 == 0 ? -n : n)` https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/introduction-to-plinq – Slai May 30 '20 at 09:49
  • You are dealing with large numbers, and so there is the possibility of arithmetic overflow. I suggest that you enclose the calculations in [`checked`](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/checked) blocks, to rule out this possibility: `checked { temp -= n; }` – Theodor Zoulias May 30 '20 at 09:53
  • @TheodorZoulias This is why I didn't just calculate plain sum of all range of integers, rather I made every other integer negative which ruled out this exception. Otherwise, I was always getting arithmetic overflow exception, even if I applied `checked` operator. Actually, because of unable to solve that problem, I changed my code to make every alternate number negative. – teenup May 30 '20 at 12:59
  • @teenup hmm, the compiler option "Check for arithmetic overflow/underflow" is not seem to be enabled by default, so normally you shouldn't get exceptions unless you use the `checked` keyword. Anyway, if this was a real production application, you should probably use a `long` accumulator instead of `int`, to make the possibility of overflow very tiny. – Theodor Zoulias May 30 '20 at 13:40

1 Answers1

2

It's because of this line:

int skip = ((i - 1) * count) / threads;
int take = count / threads;

consider threads = 3 and count = 10 It can't cover all of your list. Based on thread count you get different roundings in take and skip and sometimes they can't cover all items in the list.

You maybe should change it this way:

for(int i = 1; i <= threads; i++)
{
    int skip = ((i - 1) * count) / threads;
    int take = count / threads;

    if(i == threads)
        take = threads - skip;

    l.Add(GetSum(intList, skip, take));
}
Arman Ebrahimpour
  • 4,252
  • 1
  • 14
  • 46
  • 1
    You have guessed the problem correctly. I solved it using this `int skip = (i - 1) * take;`, with another change for `take` if it is a final iteration. Thanks. – teenup May 30 '20 at 09:43