问题
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