并行执行策略是否要求迭代器引用类型为左值?
C++并行算法对非左值范围无法并行的标准合规性问题
我排查到一个代码性能问题:对非左值范围调用带并行执行策略的算法时无法实现并行。以下是演示该行为的代码:
#include <chrono> #include <iostream> #include <thread> #include <algorithm> #include <execution> #include <ranges> #include <tbb/parallel_for.h> struct Timer { Timer() : start{std::chrono::high_resolution_clock::now()} {} ~Timer() { auto stop = std::chrono::high_resolution_clock::now(); std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start) << '\n'; } std::chrono::time_point<std::chrono::high_resolution_clock> start; }; auto fn = [](int) { using namespace std::chrono_literals; std::this_thread::sleep_for(1ms); }; void run(std::ranges::range auto&& rng) { Timer timer; std::for_each(std::execution::par, rng.begin(), rng.end(), fn); } int main() { constexpr int N = 200; run(std::views::iota(0, N)); // 无法并行 run(std::array<int, N>{}); // 可以并行 run(std::array<int, N>{} | std::views::transform([](auto& elem) { return elem * 2; })); // 无法并行 run(std::array<int, N>{} | std::views::transform([](auto& elem) -> int& { return elem; })); // 可以并行 { Timer timer; tbb::parallel_for(0, N, fn); // 可以并行 } return 0; }
假设并行函数仅需要const访问(这非常常见),第三种情况的限制是否符合C++标准要求?
这种限制是符合C++标准要求的。
核心原因在于C++标准对并行算法的迭代器和范围有明确约束:
- 使用
std::execution::par这类并行执行策略时,算法要求迭代器必须是随机访问迭代器,同时范围需要支持安全的分割与多线程访问。 - 第三种情况中,
transform视图返回的是临时int值,对应迭代器的引用类型是右值(而非左值引用)。每次解引用迭代器都会生成新的临时对象,并行算法无法保证多线程访问时的语义一致性与执行效率——标准并没有强制要求实现必须支持对这类非左值范围的并行处理,因此标准库会选择退化为串行执行。
哪怕并行函数只需要const访问也无法改变这个结果,因为问题的根源不是元素的可修改性,而是迭代器能否提供稳定的左值引用,让多个线程可以安全高效地访问元素。
内容的提问来源于stack exchange,提问作者MarkB
相关产品推荐
相关产品推荐

