zoukankan      html  css  js  c++  java
  • 【CUDA并行编程之七】数组元素之和


    现在需要求得一个数组的所有元素之和,之前感觉似乎不太可能,因为每个线程只处理一个元素,无法将所有元素联系起来,但是最近学习了一段代码可以实现,同时也对shared memory有了进一步的理解。


    一、C++串行实现

    串行实现的方法非常之简单,只要将所有元素依次相加就能够得到相应的结果,实际上我们注重的不是结果,而是运行的效率。那么代码如下:

    array_sum.cc:

    1. #include<iostream>  
    2. #include<stdio.h>  
    3. #include "kmeans.h"  
    4.   
    5. using namespace std;  
    6.   
    7. const int cnt = 100000;  
    8.   
    9. int main()  
    10. {  
    11.     int *a = new int[cnt];  
    12.   
    13.     for(int i=0;i<cnt;i++)  
    14.     {  
    15.         a[i] = i+1;   
    16.     }  
    17.   
    18.     double t = wtime();  
    19.       
    20.     for(int i=0;i<cnt;i++)  
    21.         sum += a[i];  
    22.       
    23.     printf("computation elapsed %.8f  ",wtime()-t);  
    24.   
    25.     return 0;  
    26. }  

    wtime.cu:

    1. #include <sys/time.h>  
    2. #include <stdio.h>  
    3. #include <stdlib.h>  
    4.   
    5. double wtime(void)   
    6. {  
    7.     double          now_time;  
    8.     struct timeval  etstart;  
    9.     struct timezone tzp;  
    10.   
    11.     if (gettimeofday(&etstart, &tzp) == -1)  
    12.         perror("Error: calling gettimeofday() not successful. ");  
    13.   
    14.     now_time = ((double)etstart.tv_sec) +              /* in seconds */  
    15.                ((double)etstart.tv_usec) / 1000000.0;  /* in microseconds */  
    16.     return now_time;  
    17. }  
    运行结果:



    二、CUDA并行实现

    先上代码然后再进行解释:

    1. #include <iostream>  
    2. #include <stdio.h>  
    3. #include "kmeans.h"  
    4.   
    5. using namespace std;  
    6.   
    7. const int count = 1000;  
    8.   
    9. void generate_data(int *arr)  
    10. {  
    11.     for(int i=0;i<count;i++)   
    12.     {  
    13.         arr[i] = i+1;  
    14.     }  
    15. }  
    16.   
    17. int nextPowerOfTwo(int n)  
    18. {  
    19.     n--;  
    20.     n = n >> 1 | n;  
    21.     n = n >> 2 | n;  
    22.     n = n >> 4 | n;  
    23.     n = n >> 8 | n;  
    24.     n = n >> 16 | n;  
    25.     //n = n >> 32 | n; //For 64-bits int   
    26.     return ++n;  
    27. }  
    28.   
    29. /* 
    30. cnt : count  
    31. cnt2 : next power of two of count  
    32. */  
    33. __global__ static void compute_sum(int *array,int cnt , int cnt2)  
    34. {  
    35.     extern __shared__ unsigned int sharedMem[];  
    36.     sharedMem[threadIdx.x] = (threadIdx.x < cnt) ? array[threadIdx.x] : 0 ;  
    37.     __syncthreads();  
    38.   
    39.     //cnt2 "must" be a power of two!  
    40.     for( unsigned int s = cnt2/2 ; s > 0 ; s>>=1 )  
    41.     {  
    42.         if( threadIdx.x < s )      
    43.         {  
    44.             sharedMem[threadIdx.x] += sharedMem[threadIdx.x + s];  
    45.         }  
    46.         __syncthreads();  
    47.     }  
    48.     if(threadIdx.x == 0)  
    49.     {  
    50.         array[0] = sharedMem[0];      
    51.     }  
    52. }  
    53.   
    54.   
    55. int main()  
    56. {  
    57.     int *a = new int[count];  
    58.     generate_data(a);  
    59.   
    60.     int *deviceArray;  
    61.     cudaMalloc( &deviceArray,count*sizeof(int) );  
    62.     cudaMemcpy( deviceArray,a,count*sizeof(int),cudaMemcpyHostToDevice );  
    63.     int npt_count = nextPowerOfTwo(count);//next power of two of count  
    64.     //cout<<"npt_count = "<<npt_count<<endl;  
    65.     int blockSharedDataSize = npt_count * sizeof(int);  
    66.   
    67.     double t = wtime();  
    68.     for(int i=0;i<count;i++)  
    69.     {  
    70.         compute_sum<<<1,count,blockSharedDataSize>>>(deviceArray,count,npt_count);  
    71.     }  
    72.     printf("computation elapsed %.8f  ",wtime()-t);  
    73.   
    74.     int sum ;  
    75.     cudaMemcpy( &sum,deviceArray,sizeof(int),cudaMemcpyDeviceToHost );  
    76.     cout<<"sum = "<<sum<<endl;  
    77.       
    78.     return 0;  
    79. }  


    主函数:
    line58:
    为数组a赋初值,维度为count。

    line60~62:定义device变量并分配内存,将数组a的值拷贝到显存上去。

    line63:nextPowerOfTwo是非常精妙的一段代码,它计算大于等于输入参数n的第一个2的幂次数。至于为什么这么做要到kernel函数里面才能明白。

    line68:compute_sum中的"1"为block的数量,"count"为每个block里面的线程数,"blockSharedDataSize"为共享内存的大小。

    核函数compute_sum:

    line35:定义shared memory变量。

    line36:将threadIdx.x小于cnt的对应的sharedMem的内存区赋值为数组array中的值。

    line39~47:这段代码的功能就是将所有值求和之后放到了shareMem[0]这个位置上。这段代码要好好体会一下,它把原本计算复杂度为O(n)的串行实现的时间效率通过并行达到了O(logn)。最后将结果保存到array[0]并拷贝回主存。

    makefile:

    1. cu:  
    2.     nvcc cuda_array_sum.cu wtime.cu  
    3.     ./a.out  
    结果:



    三、效率对比

    我们通过修改count的值并且加大循环次数来观察变量的效率的差别。

    代码修改:




    运行时间对比:

    count
    串行(s)
    并行(s)
    1000
    0.00627995
    0.00345612
    10000
    0.29315591
    0.06507015
    100000
    25.18921304
    0.65188980
    1000000
    2507.66827798
    5.61648989



    哈哈,可见在数据量大的情况下效率还是相当不错的。

    Author:忆之独秀

    Email:leaguenew@qq.com

    注明出处:http://blog.csdn.net/lavorange/article/details/43031419


  • 相关阅读:
    [转]关于php后门的编写
    PHP写txt日志换行
    AngularJS 前端JS框架
    跨域上传
    [转] 多域名THINKPHP利用MEMCACHE方式共享SESSION数据
    关于TP的 文件目录安全
    关于浏览器内部和 手机浏览器 上传兼容
    [转]php计算到指定日期还有多少天的方法
    vi/vim下看十六进制文件
    dos2unix(windows脚本文件放到unix下运行要注意)
  • 原文地址:https://www.cnblogs.com/walccott/p/4956933.html
Copyright © 2011-2022 走看看