Thrust快速入门教程(四) —— Fancy Iterators
高级迭代器可以实现多种有价值功能。本节将展示如何利用高级迭代器和标准Thrust算法处理一个更广泛的类问题。对于那些熟悉的Boost C ++库的开发者,他们会发现Thrust的高级迭代器与Boost迭代器库非常相似。
constant_iterator 常量迭代器最明显的特点,是每次解引用时,都会返回一个相同的值。下面的例子我们将一个常量迭代器的初始值设置为10。
#include
...
// create iterators
thrust::constant_iterator first(10);
thrust::constant_iterator last = first + 3;
first[0]// returns 10
first[1]// returns 10
first[100] // returns 10// sum of [first, last)
thrust::reduce(first, last);
// returns 30 (i.e. 3 * 10)
当需要输入恒值序列时,常量迭代器将会是最便捷、高效的解决方案。
counting_iterator 如果一个序列需要不断增长的值,计数迭代器将是一个很好的选择。下面的例子将计数迭代器的初始值设定为10,并像数组一样访问该迭代器。
#include
...
// create iterators
thrust::counting_iterator first(10);
thrust::counting_iterator last = first + 3;
first[0] // returns 10
first[1] // returns 11
last[0]// returns 13
first[100] // returns 110// sum of [first, last)
thrust::reduce(first, last);
// returns 33 (i.e. 10 + 11 + 12)
虽然常量迭代器和计数迭代器可以以数组形式访问,但实际上它们并不需要存储器开销。每次解引用某个迭代器时,它会生成对应的值,并将该值返回给调用函数。
transform_iterator 在算法部分的教程中讲到过kernel融合,比如将transform算法和reduce算法,组合成单一的
transform_reduce
操作。即使没有专门的transform_xxx
算法类型,转换迭代器同样可以实现一样的功能。下面的例子展示了另一种融合transformation算法和reduction算法的方式。
#include
// initialize vector
thrust::device_vector vec(3);
vec[0] = 10;
vec[1] = 20;
vec[2] = 30;
// create iterator (type omitted)
... first = thrust::make_transform_iterator(vec.begin(), negate());
... last= thrust::make_transform_iterator(vec.end(),negate());
first[0]// returns -10
first[1]// returns -20
first[2]// returns -30// sum of [first, last)
thrust::reduce(first, last);
// returns -60 (i.e. -10 + -20 + -30)
简单起见,这里省略了第一个和最后一个迭代器的类型,用
...
代替。转换迭代器的缺点之一,是它返回的迭代器类型很长(这里省略的...
为thrust::transform_iterator, thrust::device_vector::iterator>
)。针对这个问题,通常的解决方法是:只要把make_transform_iterator
放在被调用算法的参数里。例如:// sum of [first, last)
thrust::reduce(thrust::make_transform_iterator(vec.begin(), negate()),
thrust::make_transform_iterator(vec.end(),negate()));
这样就避免了创建变量来存储第一个和最后一个迭代器。
permutation_iterator 上一节展示了如何使用转换迭代器融合transformation算法和另一种算法,从而避免不必要的内存操作。类似的,排列迭代器可以将分散(scatter)、聚集(gather)操作和Thrust算法进行融合,甚至和其他高级迭代器也可以。
下面的示例演示如何将聚合操作和reduction算法进行融合:
#include
...
// gather locations
thrust::device_vector map(4);
map[0] = 3;
map[1] = 1;
map[2] = 0;
map[3] = 5;
// array to gather from
thrust::device_vector source(6);
source[0] = 10;
source[1] = 20;
source[2] = 30;
source[3] = 40;
source[4] = 50;
source[5] = 60;
// fuse gather with reduction:
//sum = source[map[0]] + source[map[1]] + ...
int sum = thrust::reduce(thrust::make_permutation_iterator(source.begin(), map.begin()),
thrust::make_permutation_iterator(source.begin(), map.end()));
这里使用了
make_permutation_iterator
函数来构建排列迭代器。make_permutation_iterator
的第一个参数是聚合操作的源数组,第二个参数是索引数组。两次调用的make_permutation_iterator
函数中,第一个参数是一样的,但是第二个参数是不同的,用来定义索引数组的开头和结尾。当排列迭代器作为一个函数的输出序列时,它相当于分散操作与该函数的融合。通常排列迭代器允许对序列的特定的值进行操作,而不需要对整个序列进行操作。注:
文章图片
zip_iterator 最好的迭代器总是放在最后!zip迭代器是一个非常实用的小工具:它将多个输入序列用来产生一个元组(
tuple
)序列。下面的示例是将整型序列和字符型序列“压缩”成一个元组序列,并且计算这个元组的最大值。
#include
...
// initialize vectors
thrust::device_vectorA(3);
thrust::device_vector B(3);
A[0] = 10;
A[1] = 20;
A[2] = 30;
B[0] = 'x';
B[1] = 'y';
B[2] = 'z';
// create iterator (type omitted)
first = thrust::make_zip_iterator(thrust::make_tuple(A.begin(), B.begin()));
last= thrust::make_zip_iterator(thrust::make_tuple(A.end(),B.end()));
first[0]// returns tuple(10, 'x')
first[1]// returns tuple(20, 'y')
first[2]// returns tuple(30, 'z')// maximum of [first, last)
thrust::maximum< tuple > binary_op;
thrust::tuple init = first[0];
thrust::reduce(first, last, init, binary_op);
// returns tuple(30, 'z')
zip迭代器之所以非常实用,是因为大多数算法只能容纳一个输入序列,或者最多可以容纳两个。zip迭代器将多个独立的序列合并成一个单一的元组序列,使更多算法能够处理这些序列。
如何利用zip迭代器和
for_each
函数实现三元transformation,请参阅代码实例arbitrary_transformation。只要仿照这个代码实例做一些简单扩展,就可以实现多个输出序列的transformation。zip迭代器除了方便,还能使程序更有效地运行。例如,在CUDA里,将三维空间的点储存为
float3
型的数组是糟糕的,因为在访问操作时不能合并存储器的访问。使用zip迭代器,可以将三个坐标分别存储在三个不同的数组中,这样就可以合并访存。在这种情况下使用zip迭代器创建一个包含三维向量的(虚拟)数组,再作为thrust算法的参数。
更多细节请参阅代码实例dot_products_with_zip。
【Thrust快速入门教程(四) —— Fancy Iterators】注:a是结构体数组,b是数组结构体。zip迭代器可以创建一个数组结构体,可以实现合并访存。
文章图片
参考:
- Thrust快速入门:五大高级迭代器
- Efficient Gather and Scatter Operations on Graphics Processors
推荐阅读
- neo4j|neo4j cql语句 快速查询手册
- 通过复盘快速成长(附模板)
- jar|springboot项目打成jar包和war包,并部署(快速打包部署)
- 快速阅读作业【2/21】《阅读(革命性新定义》)
- Spring注解05|Spring注解05 @Import 给容器快速导入一个组件
- 虐虐快速通关秘籍放这,高度概括就是听话照做!
- Node入门教程(11)第九章(Node|Node入门教程(11)第九章:Node 的网络模块)
- 想要用正确的方法快速进阶,这三个方法推荐给你。
- vue快速上手-3
- arthas快速入门视频演示