Is there a way of making a lock-free “counter” random access iterator in C++?

末鹿安然 提交于 2020-01-05 08:32:53

问题


I'm experimenting with the execution policies on a toy problem of evaluating a polynomial at a certain point, given its coefficients and the point of evaluation (x).

Here's my implementation:

class counter: public std::iterator<
        std::random_access_iterator_tag,   // iterator_category
        size_t,                      // value_type
        size_t,                      // difference_type
        const size_t*,               // pointer
        size_t                       // reference
>{
    size_t num = 0;
public:
    explicit counter(size_t _num) : num(_num) {}
    counter& operator++() {num += 1; return *this;}
    counter operator++(int) {counter retval = *this; ++(*this); return retval;}
    bool operator==(counter other) const {return num == other.num;}
    bool operator!=(counter other) const {return !(*this == other);}
    counter& operator+=(size_t i) { num += i; return *this; }
    counter& operator-=(size_t i) { num -= i; return *this; }
    counter operator +(counter &other) const { return counter(num + other.num);}
    counter operator -(counter &other) const { return counter(num - other.num); }
    counter operator +(size_t i) const { return counter(num + i); }
    counter operator -(size_t i) const {return counter(num - i); }
    reference operator*() const {return num;}
};


double better_algorithm_polinomials(const vector<double> & coeffs, double x) {
    return transform_reduce(execution::par, cbegin(coeffs), end(coeffs), counter(0), 0.0, plus{}, [x](double coeff, size_t index) { return coeff * pow<double>(x, index); });
}

This works fine for the par policy, but for the par_unseq this fails due to race conditions.

I attempted to mitigate them using atomic_size_t, but there are some places (such as copy construction, or ++(int) operator), where I'm not atomic and probably have to use locks... I was wondering if there's a better way.

This doesn't work:

class counter: public std::iterator<
        std::random_access_iterator_tag,   // iterator_category
        atomic_size_t,                      // value_type
        atomic_size_t,                      // difference_type
        const atomic_size_t*,               // pointer
        atomic_size_t                       // reference
>{
    atomic_size_t num = 0;
public:
    explicit counter(size_t _num) : num(_num) {}
    counter(counter &other) {  num = other.num.load();}
    counter& operator++() {num += 1; return *this;}
    const counter operator++(int) {num += 1; return counter(num-1);}
    bool operator==(counter &other) const {return num == other.num;}
    bool operator!=(counter &other) const {return !(*this == other);}
    counter& operator+=(size_t i) { num += i; return *this; }
    counter& operator-=(size_t i) { num -= i; return *this; }
    counter operator +(counter &other) const { return counter(num + other.num);}
    difference_type operator -(counter &other) const { return num - other.num; }
    counter operator +(size_t i) const { return counter(num + i); }
    difference_type operator -(size_t i) const {return num - i; }
    size_t operator [](size_t i) const {return i;}
    reference operator*() const {return num.load();}
};

回答1:


I attempted to mitigate them using atomic_size_t

Note that you should statically assert that std::atomic<std::size_t> is actually lock-free in your platform.

but there are some places (such as copy construction, or ++(int) operator), where I'm not atomic and probably have to use locks...

You cannot acquire locks when using unsequenced execution policies! See [algorithms.parallel], e.g. this and this.



来源:https://stackoverflow.com/questions/57638255/is-there-a-way-of-making-a-lock-free-counter-random-access-iterator-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!