【问题标题】:Is there a way of making a lock-free "counter" random access iterator in C++?有没有办法在 C++ 中制作无锁“计数器”随机访问迭代器?
【发布时间】:2019-08-24 13:09:15
【问题描述】:

在给定系数和评估点 (x) 的情况下,我正在尝试在某个点评估多项式的​​玩具问题的执行策略。

这是我的实现:

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); });
}

这适用于 par 策略,但对于 par_unseq,由于竞争条件而失败。

我尝试使用 atomic_size_t 来缓解它们,但是在某些地方(例如复制构造或 ++(int) 运算符)我不是原子的,可能必须使用锁...我想知道是否有更好的办法。

这不起作用:

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();}
};

【问题讨论】:

  • 我很难看到你在做什么......
  • 我正在尝试以某种方式同时告诉转换正在处理哪个索引。最简单的方法是提供由 iota 填充的第二个向量,但这对我来说似乎非常浪费,必须分配所有额外的内存,填充它,然后为每个函数调用删除它......我认为是一个随机迭代器可以做到这一点,但我不能让它无序地工作。我正在寻找迭代器方法的修复方法或不涉及创建整个索引向量的不同方法。

标签: c++ c++17 atomic


【解决方案1】:

我尝试使用 atomic_size_t 缓解它们

请注意,您应该静态断言 std::atomic&lt;std::size_t&gt; 在您的平台中实际上是无锁的。

但在某些地方(例如复制构造或 ++(int) 运算符),我不是原子的,可能必须使用锁...

使用无序执行策略时,您无法获取锁!参见 [algorithms.parallel],例如thisthis

【讨论】:

  • 关于检查原子性的好主意,它在我的平台上确实是原子的,但是即使底层计数器是我的迭代器仍然不是原子的......有没有办法让它成为原子?
  • @Mr.WorshipMe 嗯...我正在尝试准确了解目前的要求。例如,阅读this 似乎算法不会不同步地调用迭代器的函数,该函数会多次修改迭代器,但我不确定。我会问一个关于那个的问题。顺便问一下,您使用哪个编译器/stdlib 来测试这个?
  • 使用 gcc 9.1 和 tbb 2019 u8。你能想出另一种方法让 reduce_transform 知道它正在处理哪个索引,即不会浪费地将索引作为系数向量 \ 的一部分保存在另一个向量中吗?
  • @Mr.WorshipMe 我猜你可以创建一个迭代器类,将迭代器包装到vector&lt;double&gt;,但返回值加上元组中的索引。然后你也可以简单地使用std::reduce
  • 听起来是一种有趣的方法,但是知道迭代器包装器在哪个索引上似乎是我现在正在努力解决的同一个问题......你能提供一个例子吗?
猜你喜欢
  • 2010-10-03
  • 2019-12-06
  • 2020-05-25
  • 2021-01-12
  • 1970-01-01
  • 2022-11-14
  • 1970-01-01
  • 1970-01-01
  • 2016-10-16
相关资源
最近更新 更多