Search code examples
c++openmp

Making non const object member function usable inside openMP


I want to parallelize some loops, and use openMP extensively in other parts of the code. There is an object I want to use inside a loop that has some backing storage (should be shared across threads) and some state (should be private to each thread).

Imagine something like:

class P {
    public:
        // fills storage with 0, 1, 2...
        P(size_t something) {
            for(size_t i = 0; i < something; i++)
                m_storage.push_back(i);
        }

        // finds the closest value in m_storage to probe
        // the algorithm is a stupid dummy, but gets the point across
        float findClosest(float probe) {
            m_state = std::numeric_limits<float>::max();
            auto closest = std::numeric_limits<float>::quiet_NaN();

            for(const auto x: m_storage)
                if(m_state > probe - x) {
                    closest = x;
                    m_state = probe - x;
                }

             return closest;
        }

    private:
        std::vector<float> m_storage;
        float m_state;
}

// sequential access
int main(){
    P p = new P(100);
    std::vector<float> results(5);
    for(size_t i = 0; i<5; i++) {
        results[i] = p.findClosest(i);
    }
}

// parallel access with copy => p.m_storage is copied
int main(){
    P p = new P(100);
    std::vector<float> results(5);
#pragma omp parallel for firstprivate(p)
    for(size_t i = 0; i<5; i++) {
        results[i] = p.findClosest(i);
    }
}

// parallel access with share => p.m_state is altered by multiple threads
int main(){
    P p = new P(100);
    std::vector<float> results(5);
#pragma omp parallel for firstprivate(p)
    for(size_t i = 0; i<5; i++) {
        results[i] = p.findClosest(i);
    }
}

So either I waste a lot of memory and cache space, or the implementation breaks because of shared usage of a variable.

I guess I cannot mark parts of the object shared and others private or anything like this. The object is rather complex and state is used in different places. Is there any magic way to make the object share its storage, but have state per object?

e.g. something like

[...]
    private:
#pragma omp parallel shared
        std::vector<float> m_storage;
#pragma omp parallel private
        float m_state;

Solution

  • Separate your data into two classes: one holds all the data to be shared among threads, the other the per-thread private data. The per-thread class will hold a const pointer or reference to the common data (const since you don't want to be making changes to it).

    Something like:

    class common {
    public:
        std::vector<float> m_storage;
        // ...
    };
    
    class private_data {
    public:
        private_data(const common *cstorage): storage(cstorage) { }
        const common *storage;
        float findClosest(float probe) {
            // ...
        }
    };
    

    The common class would have one instance, passed to all the private_data classes.