i am using OpenMP reduction(min) to get the minimum number of lock acquisitions in a parallel section over all participating threads. When printing each threads counter inside the parallel section, I get correct results but after the parallel section, the min counter is at 0. This is the code:
int counter = 0, maxV, minV;
bool _continue = true; //variable to indicate when one second is over
#pragma omp parallel private(id) shared(lock, _continue) reduction(min:minV) reduction( :counter) reduction(max:maxV)
{
id = omp_get_thread_num();
if(id == 0)
{
auto start = std::chrono::high_resolution_clock::now(); //start time measurement
while(_continue)
{
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::micro> elapsed = finish - start;
if(elapsed.count() > std::chrono::microseconds(sec).count())
{
_continue = false;
}
}
}
else
{
while(_continue) //during timeframe, try to obtain lock as often as possible
{
lock.lock(id);
lock.unlock(id);
counter ;
}
minV = counter;
maxV = counter;
}
#pragma omp critical
{
std::cout << id << ": " << minV << std::endl;
}
}
std::cout << minV << std::endl;
The typical output is something like:
0: 2147483647
7: 256985
4: 255973
1: 256975
6: 255740
5: 256658
3: 256856
2: 256943
0
So even for thread 0, the thread that is not acquiring the lock but just taking the time, the minV is correctly initialized as largest representable number in the reduction list item type (int).
Depending on what compiler options I choose, I can alter the result of minV (last line in cout) to the following values (always wrong...):
minV = 32767 | -std=c 14 -fopenmp
minV = 0 | -std=c 14 -fopenmp -O3
minV = 32766 | -std=c 14 -fopenmp -Wall
minV = 32767 | -std=c 14 -fopenmp -Wall -pedantic -march=native -fconcepts
So I assume it has something to do with my compiler options? Strangely though, the max and sum reduction appear to work fine...
Any help is appreciated.
CodePudding user response:
You have a confusion between the init value that OMP uses internally, and the init value from the user code. The partial reductions are done with the internal value, but at some point there also has to be a reduction against the user-supplied initial value. If you don't set that, anything can happen.
Here is a cute picture: https://theartofhpc.com/pcse/omp-reduction.html#Initialvalueforreductions
CodePudding user response:
So according to the OpenMO docs (openmp.org/spec-html/5.0/openmpsu107.html) i thought the private value is initialized as largest representable int.
The documentation you refer to talks about the initial value of the resulting minimum. It does not refer to the value that will be used to find the minimum.
Without omp:
unsigned min = 0; // initialized with minimum value
unsigned minV; // not initialized
for (int id = 0; i < 10; i) {
if (id == 0) {
// do something
} else {
minV = get_some_value(); // minV now has a value
}
min = std::min(min,minV); // !!UB!! uses uninitialized minV when id == 0 !
}
What openmp initializes corresponds to min
in this example, but your code (and this one) fails to initialize minV
for id == 0
.