Thrust快速入门教程(三)——算法 2

Reductions

Reduction算法使用二元操作将输入序列规约为一个单值。例如,需要获得一数列的和,可以通过加运算规约此数组得到。相似的,数列的最大值,可以通过由两个输入值返回一个最大值的运算子规约得到。数列的求和的规约操作可以由thrust::reduce如下实现:

  1. int sum = thrust :: reduce (D. begin () , D. end () , ( int ) 0, thrust :: plus <int >());  
 

 

开始的两个参数定义了需要规约的数组,第三和第四个参数分别提供了初始值和相关的规约操作。实际上,通常使用的时候我们选择默认情况下没有初始值和不特别指出规约方法。所以下面三行代码是等同的:

  1. int sum = thrust :: reduce (D. begin () , D. end () , ( int ) 0, thrust :: plus <int >());  
  2. int sum = thrust :: reduce (D. begin () , D. end () , ( int ) 0);  
  3. int sum = thrust :: reduce (D. begin () , D. end ())  
 

 

虽然thrust::reduce能够有效的满足大部分的规约操作,但是,Thrust库依然提供了另外的一些函数以便使用(类似于STL)。例如,thrust::count能够返回给定序列的特定值的数量。

  1. # include <thrust / count .h>   
  2. # include <thrust / device_vector .h>   
  3. ...  
  4. // put three 1s in a device_vector   
  5. thrust :: device_vector <int > vec (5 ,0);  
  6. vec [1] = 1;  
  7. vec [3] = 1;  
  8. vec [4] = 1;  
  9. // count the 1s   
  10. int result = thrust :: count ( vec . begin () , vec .end () , 1);  
  11. // result is three  
 

 

另一些规约操作,包括thrust::count_ifthrust::min_elementthrust::max_elementthrust::is_sortedthrust::inner_product等,详细请参考documentation

Transformations篇章中的SAXPY例子使用transformation内核展示了融合内核如何来减少内存交换。我们也可以使用thrust::transform_reduce实现融合内核来规约。下面的例子用来计算向量的模:

  1. # include <thrust / transform_reduce .h>   
  2. # include <thrust / functional .h>   
  3. # include <thrust / device_vector .h>   
  4. # include <thrust / host_vector .h>   
  5. # include <cmath >   
  6. // square <T> computes the square of a number f(x) -> x*x   
  7. template <typename T>  
  8. struct square  
  9. {  
  10. __host__ __device__  
  11. T operator ()( const T& x) const {  
  12. return x * x;  
  13. }  
  14. };  
  15. int main ( void )  
  16. {  
  17. // initialize host array   
  18. float x [4] = {1.0 , 2.0 , 3.0 , 4.0};  
  19. // transfer to device   
  20. thrust :: device_vector <float > d_x (x, x + 4);  
  21. // setup arguments   
  22. square <float > unary_op ;  
  23. thrust :: plus <float > binary_op ;  
  24. float init = 0;  
  25. // compute norm   
  26. float norm = std :: sqrt ( thrust :: transform_reduce ( d_x . begin () , d_x . end () ,  -  
  27. unary_op , init , binary_op ) );  
  28. std :: cout << norm << std :: endl ;  
  29. return 0;  
  30. }  
 

 

这里有一个叫平方的一元操作,将输入序列的每个元素平方。然后,平方的和由标准的加法规约操作得到。类似较慢版本的SAXPY transformation,我们可以这样实现:首先将原数列同伙乘法转换成平方存储在一个临时数组,然后使用加法规约。但是显然这样做会带来不必要的浪费,速度降低。通过在规约内核中融合平方操作,我们就可以获得与自己编写内核一样的高性能。

 

Prefix-Sums

并行的前追求和,也叫scan操作,与压实流、基数排序等都是并行算法的重要模块。下面的源码将举例说明使用默认加法的inclusive scan

  1. # include <thrust / scan .h>   
  2. int data [6] = {1, 0, 2, 2, 1, 3};  
  3. thrust :: inclusive_scan (data , data + 6, data ); // in - place scan   
  4. // data is now {1, 1, 3, 5, 6, 9}  
 

 

Inclusive scan的每个输出元素为输入数列的相应部分和。例如,data[2] = data[0] + data[1] + data[2]Exclusive scan类似,但是右移一个位置:

  1. # include <thrust / scan .h>   
  2. int data [6] = {1, 0, 2, 2, 1, 3};  
  3. thrust :: exclusive_scan (data , data + 6, data ); // in - place scan   
  4. // data is now {0, 1, 1, 3, 5, 6}  
 

 

现在为data[2] = data[0] + data[1]。由例子可见,inclusive_sacnexclusive_scan允许原址操作。Thrust也提供了函数transform_inclusive_scantransform_exclusive_scan可以实现在scan操作前对输入数列进行一元操作。完整的scan变体说明请参见documentation

posted on 2012-03-21 11:25  carekee  阅读(2747)  评论(0编辑  收藏  举报