4

I have an array of 921600 numbers between 0 and 255.

I need to check each number whether it's above a threshold or not.

Is it possible to check the first- and second half of the array at the same time, to cut down on run time?

What I mean is, is it possible to run the following two for loops in parallel?

for(int i = 0; i < 921600 / 2; i++)
{
    if(arr[i] > 240) counter++;
}

for(int j = 921600 / 2; j < 921600; j++)
{
    if(arr[j] > 240) counter++;
}

Thank you in advance!

4
  • 4
    Have you tried using Parallel.For? It doesn't split in two necessarily, but I think you'll get a very good boost depending on the system. Commented May 1, 2017 at 20:16
  • 1
    I'd skip the first half/second half stuff... it does not accomplish anything. Use Parallel.For Commented May 1, 2017 at 20:18
  • 2
    If you're going to be incrementing counter from different threads, make sure you do it properly using something like Interlocked.Increment(). Commented May 1, 2017 at 20:18
  • The problem with parallel is, you should do it in batch, not per element. Commented May 1, 2017 at 20:21

3 Answers 3

9

I suggest using Parallel Linq (PLinq) for this

int[] source = ...

int count = source
  .AsParallel()  // comment this out if you want sequential version
  .Count(item => item > 240);
Sign up to request clarification or add additional context in comments.

2 Comments

This sounds nice, but will probably very slow compared to the singlethreaded version. 'invoking' the threadpool thread will cost more that the compare itself.
@Jeroen van Langen: that's why I've put a comment on .AsParallel(): simple Countis not a good candidate to be perfomed in parallel. PLinq is very convenient when we have to decide should we compute in parallel or not - just add/comment out a single line.
2

What you are asking is strictly possible as per below.

int counter = 0;
var tasks = new List<Task>();
var arr = Enumerable.Range(0, 921600).ToArray();
tasks.Add(Task.Factory.StartNew(() =>
{
    for (int i = 0; i < 921600 / 2; i++)
    {
        if (arr[i] > 240) counter++;
    }
}));
tasks.Add(Task.Factory.StartNew(() =>
{
    for (int j = 921600 / 2; j < 921600; j++)
    {
        if (arr[j] > 240) counter++;
    }
}));
Task.WaitAll(tasks.ToArray());

Do not use this code! You will encounter a race condition with incrementing the integer where one thread's increment is lost due to a Read, Read, Write, Write situation. Running this in LinqPad, I ended up with counter being anything between 600000 and 800000. Obviously that range is nowhere near the actual value.

The solution to this race condition is to introduce a lock that means that only one thread can touch the variable at any one time. This negates the ability for the assignment to be multithreaded but allows us to get the correct answer. (This takes 0.042s on my machine for reference)

int counter = 0;
var tasks = new List<Task>();
var arr = Enumerable.Range(0, 921600).ToArray();
var locker = new Object();
tasks.Add(Task.Factory.StartNew(() =>
{
    for (int i = 0; i < 921600 / 2; i++)
    {
        if (arr[i] > 240)
            lock (locker)
                counter++;
    }
}));
tasks.Add(Task.Factory.StartNew(() =>
{
    for (int j = 921600 / 2; j < 921600; j++)
    {
        if (arr[j] > 240)
            lock (locker)
                counter++;
    }
}));
Task.WaitAll(tasks.ToArray());

The solution is indeed to use Parallel Linq as Dmitry has suggested:

Enumerable.Range(0, 921600).AsParallel().Count(x=>x>240);

This takes 0.031s which is quicker than our locking code and still returns the correct answer but removing the AsParallel call makes it run in 0.024s. Running a piece of code in parallel introduces a overhead to manage the threads. Sometimes the performance improvement outweighs this but a surprisingly large amount of the time it doesn't.

The moral of the story is to always run some metrics/timings of your expected data against your implementation of any code to check whether there is actually a performance benefit.

Comments

1

while googling for parallel concepts, came across your query. Might be the below little trick might help you

int n=921600/2;
for(int i=0; i<n; i++)
{
 if(arr[i]>240) counter ++;
 if(arr[n + i] > 240) counter ++;
}

1 Comment

this is called loop-unrolling and it might increase performance because it's easier for compiler. Works only on even counts though

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.