Reductions
Reduction算法使用二元操作将输入序列规约为一个单值。例如,需要获得一数列的和,可以通过加运算规约此数组得到。数列的求和的规约操作可以由thrust::reduce如下实现:
- int sum = thrust :: reduce (D. begin () , D. end () , ( int ) 0, thrust :: plus <int >());
开始的两个参数定义了需要规约的数组,第三和第四个参数分别提供了初始值和相关的规约操作。实际上,通常使用的时候我们选择默认情况下没有初始值和不特别指出规约方法。所以下面三行代码是等同的:
- int sum = thrust :: reduce (D. begin () , D. end () , ( int ) 0, thrust :: plus <int >());
- int sum = thrust :: reduce (D. begin () , D. end () , ( int ) 0);
- int sum = thrust :: reduce (D. begin () , D. end ())
虽然thrust::reduce能够有效的满足大部分的规约操作,但是,Thrust库依然提供了另外的一些函数以便使用(类似于STL)。例如,thrust::count能够返回给定序列的特定值的数量。
- # include <thrust / count .h>
- # include <thrust / device_vector .h>
- ...
- // put three 1s in a device_vector
- thrust :: device_vector <int > vec (5 ,0);
- vec [1] = 1;
- vec [3] = 1;
- vec [4] = 1;
- // count the 1s
- int result = thrust :: count ( vec . begin () , vec .end () , 1);
- // result is three
另一些规约操作,包括thrust::count_if、thrust::min_element、thrust::max_element、thrust::is_sorted、thrust::inner_product等,详细请参考documentation。
Transformations篇章中的SAXPY例子使用transformation内核展示了融合内核如何来减少内存交换。我们也可以使用thrust::transform_reduce实现融合内核来规约。下面的例子用来计算向量的模:
- # include <thrust / transform_reduce .h>
- # include <thrust / functional .h>
- # include <thrust / device_vector .h>
- # include <thrust / host_vector .h>
- # include <cmath >
- // square <T> computes the square of a number f(x) -> x*x
- template <typename T>
- struct square
- {
- __host__ __device__
- T operator ()( const T& x) const {
- return x * x;
- }
- };
- int main ( void )
- {
- // initialize host array
- float x [4] = {1.0 , 2.0 , 3.0 , 4.0};
- // transfer to device
- thrust :: device_vector <float > d_x (x, x + 4);
- // setup arguments
- square <float > unary_op ;
- thrust :: plus <float > binary_op ;
- float init = 0;
- // compute norm
- float norm = std :: sqrt ( thrust :: transform_reduce ( d_x . begin () , d_x . end () , -
- unary_op , init , binary_op ) );
- std :: cout << norm << std :: endl ;
- return 0;
- }
这里有一个叫平方的一元操作,将输入序列的每个元素平方。然后,平方的和由标准的加法规约操作得到。类似较慢版本的SAXPY transformation,我们可以这样实现:首先将原数列同伙乘法转换成平方存储在一个临时数组,然后使用加法规约。但是显然这样做会带来不必要的浪费,速度降低。通过在规约内核中融合平方操作,我们就可以获得与自己编写内核一样的高性能。
Prefix-Sums
并行的前追求和,也叫scan操作,与压实流、基数排序等都是并行算法的重要模块。下面的源码将举例说明使用默认加法的inclusive scan:
- # include <thrust / scan .h>
- int data [6] = {1, 0, 2, 2, 1, 3};
- thrust :: inclusive_scan (data , data + 6, data ); // in - place scan
- // data is now {1, 1, 3, 5, 6, 9}
Inclusive scan的每个输出元素为输入数列的相应部分和。例如,data[2] = data[0] + data[1] + data[2]。Exclusive scan类似,但是右移一个位置:
- # include <thrust / scan .h>
- int data [6] = {1, 0, 2, 2, 1, 3};
- thrust :: exclusive_scan (data , data + 6, data ); // in - place scan
- // data is now {0, 1, 1, 3, 5, 6}
现在为data[2] = data[0] + data[1]。由例子可见,inclusive_sacn与exclusive_scan允许原址操作。Thrust也提供了函数transform_inclusive_scan与transform_exclusive_scan可以实现在scan操作前对输入数列进行一元操作。完整的scan变体说明请参见documentation。