zoukankan      html  css  js  c++  java
  • CUDA 计算pi (π)

    通过简单的程序设计熟练CUDA的使用步骤

    下面是cuda代码及相关注释

    #include <stdio.h>
    #include <iostream>
    #include <time.h>
    //#include <cutil_inline.h>
    using namespace std;
    
    //*****************************************//
    //以下两部分将在设备上编译 由__global__标识;
    template<typename T> __global__ void reducePI1(T* __restrict__ d_sum, int num){
    //__restrict__ 是说从只读缓存中读取该数据,会有什么优势呢?
    //printf("blockIdx.x is %d
    ",blockIdx.x);//线程块索引,0~grid-1
    //printf("blockDim.x is %d
    ",blockDim.x);//线程块包含的线程数,这里就是<<<grid,block,size>>>中的block
    //printf("threadIdx.x is %d
    ",threadIdx.x);//每个线程块中线程的标号,0~block-1
    int id = blockIdx.x*blockDim.x + threadIdx.x;//为每个线程构建唯一标号,0~grid*block-1
    
    T temp;
    T pSum = 0;
    extern T __shared__ s_pi[];//数据存放在共享存储上,只有本线程块内的线程可以访问
    T rnum = 1.0/num;
    
    for(int i=id;i<num;i +=blockDim.x*gridDim.x){
        //每个线程计算的次数是总的次数(num)除以总的线程数(grid*block)
        temp = (i+0.5f)*rnum;
        pSum += 4.0f/(1+temp*temp);
    }
    
    s_pi[threadIdx.x] = pSum*rnum;//每个线程块中的线程会把自己计算得到的s_pi独立存储在本块的共享存储上
    __syncthreads();//等待本块所有线程计算完毕
    
    for(int i = (blockDim.x>>1);i >0;i >>= 1){
    //将本块内的 计算结果 进行累加
        if (threadIdx.x<i){
            s_pi[threadIdx.x] += s_pi[threadIdx.x+i];
        }
        __syncthreads();
    }
    //将加和的结果写到本块对应的显存中,以备reducePI2使用
    if (threadIdx.x==0)
    {
        d_sum[blockIdx.x]=s_pi[0];
    }
    
    //下面这段代码应该是在执行类似的算法但是结果会有很大偏差,并未找到原因^_^
    //if (warpSize>63){
    //    if (threadIdx.x<32){
    //        s_pi[threadIdx.x] += s_pi[threadIdx.x +32];
    //    }
    //}
    //if (threadIdx.x<16){
    //    s_pi[threadIdx.x] += s_pi[threadIdx.x +16];
    //printf("threadIdx.x 16 is %d
    ",threadIdx.x);
    //}
    //if (threadIdx.x<8){
    //    s_pi[threadIdx.x] += s_pi[threadIdx.x +8];
    //printf("threadIdx.x 8 is %d
    ",threadIdx.x);
    //}
    //if (threadIdx.x<4){
    //    s_pi[threadIdx.x] += s_pi[threadIdx.x +4];
    //printf("threadIdx.x 4 is %d
    ",threadIdx.x);
    //}
    //if (threadIdx.x<2){
    //    s_pi[threadIdx.x] += s_pi[threadIdx.x +2];
    //printf("threadIdx.x 2 is %d
    ",threadIdx.x);
    //}
    //if (threadIdx.x<1){
    //    d_sum[blockIdx.x] = s_pi[0]+s_pi[1];
    //printf("threadIdx.x 1 is %d
    ",threadIdx.x);
    //}
    
    
    
    }
    
    template<typename T> __global__ void reducePI2(T* __restrict__ d_sum, int num, T* __restrict__ d_pi){
    int id = threadIdx.x;//这个函数的线程块只有一个,线程数是grid,这里依然用id作为索引名
    extern T __shared__ s_sum[];//这个是共享内存中的,只有块内可见
    s_sum[id]=d_sum[id];//把显存中的数据装载进来
    __syncthreads();//等待装载完成
    
    for(int i = (blockDim.x>>1);i>0;i >>=1)
    //仍然采用半对半折和的方法对本块内所有线程中的s_sum进行求和
    {
        if (id<i){
            s_sum[id] += s_sum[id+i];    
        }
        __syncthreads();//等待求和完成
    }
    //将求和结果写入显存,使得cpu主机端可见
    if(threadIdx.x==0)
    {
        *d_pi =s_sum[0];
    }
    //if (warpSize>63){
    //    if (threadIdx.x<32){
    //        s_sum[threadIdx.x] += s_sum[threadIdx.x +32];
    //    }
    //}
    //if (threadIdx.x<16){
    //    s_sum[threadIdx.x] += s_sum[threadIdx.x +16];
    //}//
    //if (threadIdx.x<8){
    //    s_sum[threadIdx.x] += s_sum[threadIdx.x +8];
    //}
    //if (threadIdx.x<4){
    //    s_sum[threadIdx.x] += s_sum[threadIdx.x +4];
    //}
    //if (threadIdx.x<2){
    //    s_sum[threadIdx.x] += s_sum[threadIdx.x +2];
    //}
    //if (threadIdx.x<1){
    //    *d_pi = s_sum[0]+s_sum[1];
    //}
    
    }
    
    //**********************************************//
    //以下代码在主机上编译
    
    template <typename T> T reducePI(int num){
    
    int grid = 1024;//用来调整线程块的数量
    
    T *tmp;
    cudaMalloc((void**)&tmp,grid*sizeof(T));//在设备存储器(显存)上开辟grid*sizeof(T)大小的空间,主机上的指针tmp指向该空间
    reducePI1<<<grid,256,256*sizeof(T)>>>(tmp,num);//调用reducePI1
    //参数表示有grid个线程块,每个线程块有256个线程,每个线程块使用256*size大小的共享存储器(只有块内可以访问)
    
    //执行之后,会在tmp为首的显存中存储grid 个中间结果
    //printf("%d
    ",__LINE__);//显示代码所在行号,不知会有什么用
    T *d_PI;
    cudaMalloc((void**)&d_PI,sizeof(T));//显存中为π的计算结果开辟空间
    
    reducePI2<<<1,grid,grid*sizeof(T)>>>(tmp,grid,d_PI);//只有一个线程块,有grid个线程
    //执行后在显存中d_PI的位置存放最后结果
    T pi;//这是在主机内存上的空间
    cudaMemcpy(&pi,d_PI,sizeof(T),cudaMemcpyDeviceToHost);//从显存中将数据拷贝出来
    cudaFree(tmp);//释放相应的显存空间
    cudaFree(d_PI);
    
    return pi;
    }
    
    template <typename T> T cpuPI(int num){
    
    T sum = 0.0f;
    T temp;
    for (int i=0;i<num;i++)
    {
        temp =(i+0.5f)/num;
        sum += 4/(1+temp*temp);
    }
    return sum/num;
    
    }
    
    
    int main(){
    printf("test for compell 
    ");
    clock_t start, finish;//用来计时
    float costtime;
    start = clock(); 
    //************
    printf("cpu pi is  %f
    ",cpuPI<float>(1000000));//调用普通的串行循环计算 π
    //*************
    finish = clock();
    costtime = (float)(finish - start) / CLOCKS_PER_SEC; //单位是秒
    printf("costtime of CPU is %f
    ",costtime);
    
    start = clock();
    //************
    printf("gpu pi is %f
    ",reducePI<float>(1000000));//调用主机上的并行计算函数
    //************
    finish = clock();
    costtime = (float)(finish - start) / CLOCKS_PER_SEC; 
    printf("costtime of GPU is %f
    ",costtime);
    return 0;
    }

    编译和执行

    nvcc computePIsave.cu -I /usr/local/cuda-8.0/include -L /usr/local/cuda-8.0/lib64 -o test
    ./test

    当设定num数量少时cpu的计算耗时会比gpu短,但是随着num的增加,cpu的耗时会成比例增加,但是gpu耗时基本没有变化。

  • 相关阅读:
    <form:select>的使用
    存储过程-删除、新建索引
    java 反射常用总结
    java判断是否是数字
    jquery遍历数组添加行删除行
    oracle常用sql
    cxf (zhuan)
    linux 常用命令--个人小结一
    java发送邮件
    socket和webservice特点
  • 原文地址:https://www.cnblogs.com/simayuhe/p/7341627.html
Copyright © 2011-2022 走看看