Reductions
Reduction算法使用二元操作将输入序列规约为一个单值。例如,需要获得一数列的和,可以通过加运算规约此数组得到。相似的,数列的最大值,可以通过由两个输入值返回一个最大值的运算子规约得到。数列的求和的规约操作可以由thrust::reduce如下实现:
开始的两个参数定义了需要规约的数组,第三和第四个参数分别提供了初始值和相关的规约操作。实际上,通常使用的时候我们选择默认情况下没有初始值和不特别指出规约方法。所以下面三行代码是等同的:
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 / 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 / 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,我们可以这样实现:首先将原数列同伙乘法转换成平方存储在一个临时数组,然后使用加法规约。但是显然这样做会带来不必要的浪费,速度降低。通过在规约内核中融合平方操作,我们就可以获得与自己编写内核一样的高性能。