我在代码中发现了一个性能问题,该问题是由于在非左值范围上使用并行执行策略调用的算法缺乏并行性而导致的。
请考虑以下说明该行为的代码:
#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)); // not parallel
run(std::array<int, N>{}); // parallel
run(std::array<int, N>{} | std::views::transform([](auto& elem) { return elem * 2; })); // not parallel
run(std::array<int, N>{} | std::views::transform([](auto& elem) -> int& { return elem; })); // parallel
{
Timer timer;
tbb::parallel_for(0, N, fn); // parallel
}
return 0;
}
假设并行函数只需要 const 访问(这很常见),第三种情况似乎有限制。 标准是否要求这种行为?
在 C++17 和 C++20 中是这样,但从 C++23 开始不是这样。从技术上讲,您的调用在 C++23 之前是不正确的,因为您违反了库函数先决条件。
C++17 的措辞
如果算法的模板参数名为
、ForwardIterator
或ForwardIterator1
,则模板参数应满足前向迭代器的要求。ForwardIterator2
类或指针类型 X 满足前向迭代器的要求,如果
- 如果 X 是可变迭代器,则引用是对 T 的引用;如果 X 是常量迭代器,则引用是对 const T 的引用
C++20 的措辞
如果算法的模板参数名为
、ForwardIterator
或ForwardIterator1
,则模板参数应满足 Cpp17ForwardIterator 要求ForwardIterator2
类或指针类型 X 满足前向迭代器的要求,如果
- 如果 X 是可变迭代器,则引用是对 T 的引用;如果 X 是常量迭代器,则引用是对 const T 的引用
C++23 的措辞
如果算法的模板参数名为
、ForwardIterator
、ForwardIterator1
或ForwardIterator2
,并且要求模板参数是可变迭代器或模型,则模板参数应满足 Cpp17ForwardIterator 要求NoThrowForwardIterator
否则。forward_iterator