由于parallel for algorithm中没有基于索引的c++17,我想知道ranges::view::iota
是否可以与std::for_each
结合使用来模拟。那就是:
using namespace std;
constexpr int N= 10'000'000;
ranges::iota_view indices(0,N);
vector<int> v(N);
for_each(execution::par_unseq,indices.begin(),indices.end(),[&](int i) { v[i]= i; });
iota_view
似乎提供了对适当类型([range.iota.iterator])的随机访问:
iota_view<I, Bound>::iterator::iterator_category
的定义如下:(1.1)—如果
I
为Advanceable
建模,则iterator_category
为random_access_iterator_tag
。(1.2)—否则,如果
I
为Decrementable
建模,则iterator_category
为bidirectional_iterator_tag
。(1.3)—否则,如果
I
为Incrementable
建模,则iterator_category
为forward_iterator_tag
。(1.4)-否则,
iterator_category
是input_iterator_tag
。
以上代码正确吗?以这种方式使用iota_view
有性能上的损失吗?
编辑:我已经用range-v3,cmcstl2和Intel的PSTL做过一些测试。
使用range-v3,以上示例无法在GCC 8上编译。编译器抱怨begin
和end
具有不同的类型:
deduced conflicting types for parameter ‘_ForwardIterator’ (‘ranges::v3::basic_iterator<ranges::v3::iota_view<int, int> >’ and ‘ranges::v3::default_sentinel’)
使用cmcstl2,代码可以干净地编译,但不能并行运行。在我看来,它回退到了顺序版本,可能是因为某种程度上无法满足前向迭代器的要求(https://godbolt.org/z/yvr-M2)。
存在与PSTL有关的问题(https://github.com/intel/parallelstl/issues/22)。
答案 0 :(得分:0)
深入研究标准草案后,恐怕答案是否定的:它并非严格符合标准
ranges::iota_view
的并行版本中的for_each
。
for_each
的并行重载被声明为[alg.foreach]:
template<class ExecutionPolicy, class ForwardIterator, class Function> void for_each(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last, Function f);
另一方面,在[algorithms.requirements]中我们找到了约束:
如果算法的模板参数名为
ForwardIterator
,ForwardIterator1
或ForwardIterator2
,则模板参数应满足 Cpp17ForwardIterator 的要求。
正如Billy O'Neal在我在问题中发布的链接之一中指出的那样,明智地实施ranges::iota_view::iterator
不太可能满足“相等迭代器引用同一对象”的正向迭代器要求{{3 }}。因此,据我了解,ranges::iota_view::iterator
无法满足 Cpp17ForwardIterator 的要求,例如
boost::counting_iterator
。
但是,实际上,我希望实现会使用std::iterator_traits::iterator_category
来调度
适当的算法重载,如PSTL所做的那样。因此,我相信OP中的示例代码将按预期工作。 cmcstl2不起作用的原因可能是所使用的iterator_category
属于[iterator.cpp17],而不是std
。
答案 1 :(得分:0)
在C ++ 20中,有一个std::views::common
,其范围适用于标准的迭代器对接受算法。将输入范围转换为std::ranges::common_range
后,请使用std::ranges::begin
和std::ranges::end
函数为std::transform
或您使用的任何算法获取一对迭代器。
这是一个示例程序,假定使用C ++ 20编译器(这不是基于ranges-v3
的实现,不是)。我测试过的唯一一个(截至2020年10月)是G ++版本10。
#include <algorithm>
#include <numeric>
#include <execution>
#include <iostream>
#include <vector>
#include <ranges>
int main()
{
// A "large" number of elements (limited to ten for a reasonably small std::cout output)
constexpr int N = 10;
// Some range with a finite number of values (views::take at the end)
auto very_long_input_range = std::views::iota(0) | std::views::take(N);
// Source range converted to common_range (which supports std::begin & std::end)
auto input_range = std::ranges::common_view(very_long_input_range);
// Element processing function. E.g., if 'i' is a file name and this lambda parses it, it might be a big time-saver
auto some_complex_function = [](auto i){ return i * i; };
// Declare and allocate an output array (maybe range_value_t is an overkill here, but still)
// Using std::ranges::size(input_range) instead of N can also help generalize this code,
// but input_range must satisfy the std::ranges::sized_range concept
std::vector< std::ranges::range_value_t<decltype(input_range)> > output_array( N );
// Use C++17 std::execution::par with a pair of C++20 iterators from std::ranges
std::transform(std::execution::par,
std::ranges::begin(input_range),
std::ranges::end(input_range),
output_array.begin(),
some_complex_function);
// Test the output
for (auto p: output_array)
std::cout << p << std::endl;
}
G ++ 10(Ubuntu 20.20)的命令行为
g++-10 -std=c++2a -o ptest ptest.cpp -ltbb -lstdc++