c++parallel-processingopenmpstdvectorreduction

How to declare an OpenMP reduction for a std::vector inside a struct?


I'm trying to perform an std::vector sum reduction with an OpenMP reduction declaration for it:

// g++ -fopenmp MRE.cpp -o MRE
#include <vector>
#include <algorithm>
#include <omp.h>

struct Particles {
    std::vector<double> fx;
};

#pragma omp declare reduction(vec_double_plus : std::vector<double> : \
std::transform(omp_out.begin(), omp_out.end(), omp_in.begin(), omp_out.begin(), std::plus<double>()) \
) initializer(omp_priv = decltype(omp_orig)(omp_orig.size()))

int main() {
    Particles particles;
    
    // Initialize fx vector with 100 elements
    for (int i = 0; i < 100; ++i){
        particles.fx.push_back(0.0);
    }

    // Parallel region to set the values of fx
    #pragma omp parallel
    {
        // Thread-private local vector
        std::vector<double> thread_fx(particles.fx.size(), 0.0);

        #pragma omp for
        for (size_t i = 0; i < particles.fx.size(); ++i){
            thread_fx[i] = 1.0;  // Assign values to thread_fx
        }

        // Now we reduce thread_fx into particles.fx
        #pragma omp for reduction(vec_double_plus: particles.fx)
        for (size_t i = 0; i < particles.fx.size(); ++i) {
            particles.fx[i] += thread_fx[i];
        }
    }

    return 0;
}

...but the compiler complains about no reduction declaration for my struct, even though I'm performing the operation on an std::vector type:

MRE.cpp: In function ‘int main()’:
MRE.cpp:31:57: error: expected ‘)’ before ‘.’ token
   31 |     #pragma omp for reduction(vec_double_plus: particles.fx)
      |                                                         ^
      |                                                         )
MRE.cpp:31:48: error: user defined reduction not found for ‘particles’
   31 |     #pragma omp for reduction(vec_double_plus: particles.fx)

How do I specifically declare the reduction clause for a std::vector inside a struct?


Solution

  • You cannot reduce on struct members, but only on the whole object. I'm not sure what you try to do with the code in the parallel region, but here is a working version that reduces the vector of your Particles.

    #include <algorithm>
    #include <iostream>
    #include <omp.h>
    #include <vector>
    
    struct Particles {
      std::vector<double> fx;
      static void init(Particles &n, const Particles &o) {
        n.fx.resize(o.fx.size(), 0);
      }
    };
    
    #pragma omp declare reduction(vec_double_plus                                  \
    :Particles : std::transform(omp_out.fx.begin(), omp_out.fx.end(),              \
                                    omp_in.fx.begin(), omp_out.fx.begin(),         \
                                    std::plus<double>()))                          \
        initializer(Particles::init(omp_priv, omp_orig))
    
    int main() {
      Particles particles;
    
      // Initialize fx vector with 100 elements
      particles.fx.resize(100, 0.0);
    
    // Parallel region to set the values of fx
    #pragma omp parallel
      {
        // Thread-private local vector
        std::vector<double> thread_fx(particles.fx.size(), 0.0);
    
    #pragma omp for
        for (size_t i = 0; i < particles.fx.size(); ++i) {
          thread_fx[i] = 1.0; // Assign values to thread_fx
        }
    
    // Now we reduce thread_fx into particles.fx
    #pragma omp for reduction(vec_double_plus : particles)
        for (size_t i = 0; i < particles.fx.size(); ++i) {
          particles.fx[i] += thread_fx[i];
        }
      }
      std::cout << particles.fx[0] << ", " << particles.fx[50] << ", "
                << particles.fx[99] << std::endl;
    
      return 0;
    }
    

    For an access pattern to the vector as shown in your MRE, you would not need a reduction at all. You can safely write to a shared Particle object, because the different threads access the entries exclusively.