【问题标题】:Iterator with STL Algorithms and std::execution::par Execution Policy具有 STL 算法和 std::execution::par 执行策略的迭代器
【发布时间】:2018-07-05 11:59:56
【问题描述】:

在为 std::for_each 等 STL 算法指定 std::execution::par 执行策略时,如何使用迭代器?

在下面的示例中,我希望对 Eigen3 矩阵的对角线求平方根,如下所示:

template<typename T>
using Matrix = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;

template<typename T>
std::vector<T> getStdDev(const Matrix &x) const {
    const size_t len = x.rows();
    std::vector<T> stdDev(len);

    // Generate indices.
    size_t idx = 0;
    std::vector<size_t> indices(len);
    auto ind = [&idx]() {
        return idx++;
    };
    std::generate(indices.begin(), indices.end(), ind);

    // Take square root of diagonal elements.
    auto sr = [&x](size_t i) {
        stdDev[i] = std::sqrt(x(i, i)); 
    };
    std::for_each(std::execution::par, indices.begin(), indices.end(), sr);

    return stdDev;
}

据我所知,上面的代码是线程安全的。但是,如果不首先生成所需的索引,如何以 线程安全 方式使用迭代器实现相同的效果,如上所述?假设要迭代的容器没有实现迭代器,或者没有线程安全的迭代器

理想情况下,我希望通用地执行此操作(我知道 Eigen 具有迭代器,此处仅用于示例目的)。

此外,最好只使用 C++ 功能(没有库,但任何非草案标准)。

【问题讨论】:

  • 啊,是的,那部分是用浏览器写的。当然,这应该是串行执行的。我要编辑。

标签: c++ parallel-processing stl iterator c++17


【解决方案1】:

您可以使用 boost::counting_iteratorranges::view::iota 之类的东西

template<typename T>
std::vector<T> getStdDev(const Matrix &x) const {
    const size_t len = x.rows();
    std::vector<T> stdDev(len);

    // Take square root of diagonal elements.
    auto sr = [&x](size_t i) {
        return std::sqrt(x(i, i)); 
    };
    std::transform(std::execution::par, boost::counting_iterator<int>(0), boost::counting_iterator<int>(len), stdDev.begin(), sr);

    return stdDev;
}

自己实现counting_iterator 并不难,指定RandomAccessIterator 的所有必需成员只是很乏味。

class counting_iterator
{
public:
    typedef size_t value_type;
    typedef const size_t& reference;
    typedef const size_t* pointer;
    typedef ptrdiff_t difference_type;
    typedef random_access_iterator_tag iterator_category;

    explicit counting_iterator(size_t x);
    size_t const& base() const { return m_inc; }
    reference operator*() const { return m_inc; }
    counting_iterator& operator++() { ++m_inc; return *this; }
    counting_iterator& operator--() { --m_inc; return *this; }

    counting_iterator& operator+=(size_t i) { m_inc += i; return *this; }
    counting_iterator& operator-=(size_t i) { m_inc -= i; return *this; }

    // and loads of others
private:
    size_t m_inc; 
};

【讨论】:

  • 感谢您的回答!但是,我很抱歉,我不想使用任何外部库。我忘了指定这一点,并将相应地编辑问题。再次抱歉,我非常感谢您的回答。
  • 你可以自己轻松写counting_iterator
  • 为了避免竞争条件,但是我必须使用原子操作来更新内部计数器,这肯定会扼杀从并行化中获得的任何性能提升,对吧?
  • 没有。 counting_iterator 的每个实例都有一个单独的计数器。迭代器按值传递
  • 但是,我想知道潜在的乱序执行。例如,如果一个线程对元素 n 进行操作,则不能保证下一个元素是进程将是元素 n+1。所以,我认为必须有一些共享状态。
猜你喜欢
  • 2021-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-25
  • 1970-01-01
  • 1970-01-01
  • 2022-10-07
  • 1970-01-01
相关资源
最近更新 更多