2

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?

0

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

5 Comments

This correctly performs the reduction I wanted, but why aren't reductions on struct members allowed? The struct in my code has multiple std::vector members, some of which I need a reduction sum on, and some for which I do not. I don't think it would make sense to declare a reduction for the entire struct.
@StefandeSouza reduction is a form of scoping with specially defined behavior. Similar, as you cannot hide parts of a struct in C/C++ in a local scope, you cannot apply different OpenMP scope properties to different parts of a single object. How should it be possible to make some parts of the same struct private while making other parts shared?
aside: omp_in.fx.begin(), omp_out.fx.begin() looks sus, should that be omp_in.fx.begin(), omp_in.fx.begin()?
@Caleth No, see std::transform() (binary op overload). The last iterator argument before the functor argument is where the result is written to.

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.