Search code examples
c++multithreadingconcurrencytbb

TBB Parallel_for count, increment variable inaccurate


I am aware that with parallel processes, without proper care you can get race conditions where one thread modifies a variable that another is accessing, causing various errors and such.

I am trying to write code that makes use of a 'parallel_for', a global variable 'count' and a condition to increment 'count'.

once the condition is met, I call count++ from inside the parallel for.

for data validation I setup a parallel_reduce that also checks for the same condition and I receive a different result, which is higher than what I get with the parallel_for, which is why I know that the parallel_for is causing the issue.

Am I right in thinking that using 'count++' is still causing a race condition, as my original assumption was that count++ would work as i'm never storing a value, just at the point in time, saying just add 1 to whatever 'count' is. I want to try and avoid using mutex or locks if possible.

int count = 0;

parallel_for(blocked_range2d<int, int>(0, smallestHeight, 0, smallestWidth), [&](const blocked_range2d<int, int>&r) {
        auto y1 = r.rows().begin();
        auto y2 = r.rows().end();
        auto x1 = r.cols().begin();
        auto x2 = r.cols().end();

        for (auto y = y1; y < y2; y++) {
            for (auto x = x1; x < x2; x++) {

                int pixelValue = rgbDiffVal[y][x].rgbRed + rgbDiffVal[y][x].rgbGreen + rgbDiffVal[y][x].rgbBlue;

                if (pixelValue > bThreshold) {
                    count++;
                }
            }
        }
    });

Solution

  • tbb::parallel_for assumes that the body of the loop is thread-safe, i.e. does not have races. In this case, it does have a race, as it modifies a global variable without synchronization. The fact that modification is a mere increment of "whatever the current value is" does not matter, it is still a race (for example, some hardware might implement ++ as reading a value from memory to register, changing the register, writing new value back to memory).

    In order to fix this code, declare count as an atomic variable: std::atomic<int> if you use C++11, or tbb::atomic<int> for earlier versions of the language.