79123437

Date: 2024-10-24 19:54:31
Score: 1.5
Natty:
Report link

There are several reasons why your parallel code is slower. Let's keep in mind the main reason to use parallel threads: You have tasks that are CPU-bound and wish to divide the workload.

The problem is that your code is NOT CPU-bound in the first place. A simple loop over 100_000 iterations is not hogging the CPU. About 8 years ago, using a 3.0 GHz CPU, I found that I could go through 600_000 iterations before parallel threads would be beneficial.

Next, BitArray is a horrible example for using Parallel.For iterating 1 index at a time? As mentioned in a comment, it is not thread-safe but what I want to point out is that a BitArray is backed by a list of integers. So an individual bit in the BitArray maps to a bit on an integer. And with Parallel.For, your loop may be attempting to set individual bits belonging to the same backing integer, which causes many performance issues. Yikes!

A better example would be try using a bool[]. There would be less performance conflicts. Even still, do not expect this to perform better than a simple serial loop.

What could be done to create a loop that rivals or surpasses a serial performance? Besides using a bool[], you should try to increase the array length to half a million or more. But you will still find Parallel.For is slower.

Why? Because Parallel.For creates a new task for each index. Granted, creating a task is a relatively low-impact call but when you create 100K up to 1 million, then a lot of those little impact things add up to a big impact. You could still use parallel threads but the focus should be to create just a few, and not 100K.

You can investiage using a Range Partitioner. Or you can simple create your own chunks based on the array length. A general rule of thumb I apply is to use twice the number of CPU's. That is 2 * Environment.ProcessorCount. Divide the array length by that value to produce a range size. If it does not divide evenly, i.e. has a remainder, then add 1 to the range size. Now just create your own ranges based on that range size.

You can then process each range in parallel. Instead of creating 100K threads, you have created a much smaller amount so you have minimized the overhead of creating tasks.

If you still insist on using BitArray, then the challenge is to produce a range size that is also evenly divided by 32. Why? Because that why a backing intger will belong to only one range. While you process the many ranges in parallel, you still process a given range serially. Since the backing integer would belong to only 1 range, you do not encounter performance conflicts.

Below is a link to a Paritioner class:

https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.partitioner?view=net-8.0

Reasons:
  • Blacklisted phrase (0.5): Why?
  • Blacklisted phrase (1): What could be
  • RegEx Blacklisted phrase (1): I want
  • Long answer (-1):
  • Has code block (-0.5):
  • Contains question mark (0.5):
Posted by: Rick Davin