Using std::atomic<double> during parallel loop and update if condition meets requirements

Viewed 59

Here there is an example code with a std::atomic and a std::for_each. (C++ 17)

#include <iostream>
#include <atomic>
#include <vector>
#include <algorithm>
#include <execution>

int main()
{
    constexpr int vec_size = 2000000;
    std::vector<double> numbers(vec_size, 1.0);
    numbers[vec_size / 2] = 2.0;
    std::atomic<double> max_value{ 0.0 };
    std::for_each(std::execution::par_unseq, numbers.begin(), numbers.end(), [&max_value](double s) {
        if (s > max_value)
        {
            max_value = s;
        }
        });
    std::cout << "Max Value: " << std::fixed << max_value;
    return 0;
}

The condition could be evaluated by one thread, during that, another thread could update the max_value. How to do that correct?

I found this answer from 2013 and I wonder whether this is available built-in with C++17 or C++20 today.

1 Answers

You can do non-blocking busy wait to update max_value from multiple threads without race conditions:

// ...
#include <emmintrin.h>

int main() {
    // ...
    std::atomic<double> max_value{ 0.0 };
    auto const update_max_value = [&max_value](double s) {
        for(double expected = max_value.load(std::memory_order_relaxed);
            expected < s && !max_value.compare_exchange_weak(expected, s);)
            _mm_pause(); 
    };
    std::for_each(std::execution::par_unseq, numbers.begin(), numbers.end(), update_max_value);
    // ...
}

compare_exchange_weak reloads max_value into expected if max_value has changed since it was loaded into expected.


Updating max_value from multiple threads is not necessary for this task, you can find the maximum value in the range using any number threads (std::max_element overloads 2 and 4) and only then update max_value. This way only one thread ever loads and updates max_value, so that no std::atomic is necessary:

double max_value = *std::max_element(std::execution::par_unseq, numbers.begin(), numbers.end());
Related