C++17 中数组索引范围的并行 for 循环

use*_*445 5 c++ parallel-processing c++17

我需要更新一个 100M 元素的数组,并且希望并行执行。 std::for_each(std::execution::par, ...)看起来很棒,除了更新需要根据我正在更新的索引访问其他数组的元素。我尝试并行化的最小串行工作示例可能如下所示:

for (size_t i = 0; i < 100'000'000; i++)
    d[i] = combine(d[i], s[2*i], s[2*i+1]);
Run Code Online (Sandbox Code Playgroud)

我当然可以手动生成线程,但这比代码多得多std::for_each,因此找到一种使用标准库来执行此操作的优雅方法会很棒。到目前为止,我发现了一些不太优雅的使用方式for_each,例如:

  • 通过对数组元素的地址使用指针算术来计算索引。

  • 本着 boost 的精神实现我自己的伪造迭代器counting_range

有一个更好的方法吗?

pax*_*blo 2

您应该能够迭代索引不是项目。我认为 C++20std::ranges为您提供了一种简单的方法来做到这一点,或者您可以使用其中一种 Boostrange方法。我不知道为什么你会考虑本着Boost 的精神counting_range推出自己的产品,而你可以直接使用Boost :-)

话虽如此,我实际上选择了那种自己动手的方法,只是为了使代码独立,既不使用 C++20 也不使用 Boost:可以根据paxrange您的需要随意替换为其他方法之一:

#include <iostream>
#include <algorithm>

// Seriously, just use Boost :-)

class paxrange {
    public:
        class iterator {
            friend class paxrange;
            public:
                long int operator *() const { return value; }
                const iterator &operator ++() { ++value; return *this; }
                iterator operator ++(int) { iterator copy(*this); ++value; return copy; }

                bool operator ==(const iterator &other) const { return value == other.value; }
                bool operator !=(const iterator &other) const { return value != other.value; }

            protected:
                iterator(long int start) : value (start) { }

            private:
                unsigned long value;
        };

        iterator begin() const { return beginVal; }
        iterator end() const { return endVal; }
        paxrange(long int  begin, long int end) : beginVal(begin), endVal(end) {}
    private:
        iterator beginVal;
        iterator endVal;
};
int main() {
    // Create a source and destination collection.

    std::vector<int> s;
    s.push_back(42); s.push_back(77); s.push_back(144);
    s.push_back(12); s.push_back(6);
    std::vector<int> d(5);

    // Shows how to use indexes with multiple collections sharing index.

    auto process = [s, &d](const int idx) { d[idx] = s[idx] + idx; };
    paxrange x(0, d.size());
    std::for_each(x.begin(), x.end(), process); // add parallelism later.

    // Debug output.

    for (const auto &item: s) std::cout << "< " << item << '\n';
    std::cout << "=====\n";
    for (const auto &item: d) std::cout << "> " << item << '\n';
}
Run Code Online (Sandbox Code Playgroud)

该解决方案的“核心”是 中间的三行main(),您可以在其中设置一个回调函数,该函数采用索引而不是项目本身。

在该函数中,您可以使用该索引加上所需数量的集合来设置目标集合,这与您想要的非常相似。

就我而言,我只是希望输出向量成为输入向量,但根据输出将索引添加到每个元素:

< 42
< 77
< 144
< 12
< 6
=====
> 42
> 78
> 146
> 15
> 10
Run Code Online (Sandbox Code Playgroud)