简体   繁体   中英

Iterator with STL Algorithms and std::execution::par Execution Policy

How would one use an Iterator when specifying the std::execution::par Execution Policy for an STL algorithm such as std::for_each ?

In the below example, I wish to take the square root of the diagonal of an Eigen3 Matrix as follows:

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

As far as I am aware, the above code is thread safe . However, how would one achieve the same effect with an Iterator in a thread safe manner without generating the desired indices first, as above? The assumption is that the container to be iterated over does not have an Iterator implemented, or does not have an Iterator that is thread safe .

Ideally, I wish to do this generically (I am aware Eigen has Iterators, it is used here just for example purposes).

Additionally, it would be highly preferable to only use C++ features (no libraries, but any non draft standard).

You can use something like boost::counting_iterator or ranges::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;
}

Implementing a counting_iterator yourself isn't hard, it's just tedious to specify all the required members of 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; 
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM