<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      高性能計算-CUDA一維信號均值濾波及內存優化(22)

      1. 目標:使用CPU和GPU對一千萬數量級的一維信號進行均值濾波,并且根據GPU存儲模型對數據存儲進行優化,最終對比計算結果并計算加速比。

      2. 代碼

      /*
      cuda實現對一維信號卷積平滑濾波處理,并于串行計算對比結果和加速比,卷積核大小為5
      */
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <math.h>
      #ifdef WIN32
      #include <sys/utime.h>
      #else
      #include <sys/time.h>
      #endif
      #include "cuda_runtime_api.h"
      
      #define MAXLEN 256 
      #define QW 10000000 //數據量
      #define EP 1e-15    //精度
      #define FSIZE 5     //一維卷積核大小
      #define BlockSize 512 
      
      double* in = (double*)calloc(QW,sizeof(double));
      double* out = (double*)calloc(QW, sizeof(double));              //cpu串行計算結果
      double* outFD = (double*)calloc(QW, sizeof(double));            //gpu計算結果
      double* filter = (double*)calloc(FSIZE, sizeof(double));
      __constant__ double fliterConst[FSIZE];
      
      
      void readFile(double*in,int len)
      {
          FILE* file = fopen("./noise1qw.txt", "r");
          if (!file)
          {
              perror("打開noise失敗");
              return;
          }
          //fgets讀取一行包含換行符和字符串結束字符\0,這里使用fscanf讀取遇到空格和換行符結束
          int i = 0;
          while (fscanf(file,"%lf",in+i++) >0)
          {
              //printf("%1.16f\n", in[i - 1]);
              //if (i == 2)
                  //break;
          }
          fclose(file);
      }
      
      void saveFile(double* out, int len)
      {
          FILE* file = fopen("./result.txt", "w");
          if (file == NULL)
          {
              perror("open result.txt failed.\n");
              return;
          }
          for (int i = 0;i < len;i++)
              fprintf(file, "%2.16f\n", out[i]);
          fclose(file);
      }
      
      //ms
      long getTime()
      {
          struct timeval cur;
          gettimeofday(&cur, NULL);
          // printf("sec %ld usec %ld,toal ms %ld\n",cur.tv_sec,cur.tv_usec,cur.tv_sec*1e3 + cur.tv_usec / 1e3);
          return cur.tv_sec*1e3 + cur.tv_usec / 1e3;
      }
      
      //串行卷積
      void serialConv(double* in,double* out,size_t len,double* filter,size_t filterSize)
      {
          for (int i = 0;i < len;i++)
          {
              int start = i - FSIZE / 2;
              double result = 0.0;
              for (int j = 0;j < FSIZE;j++)
              {
                  int offPos = start + j;
                  if (offPos >= 0 && offPos < len)
                      result += filter[j] * in[offPos];
              }
              out[i] = result;
          }
      }
      
      //gpu并行卷積
      __global__ void kernalConv(double* in,double* out, size_t len, double* filter, size_t filterSize)
      {
          int id = blockIdx.x * blockDim.x + threadIdx.x;
          int start = id - FSIZE / 2;
          double result = 0.0;
          for (int i = 0;i < FSIZE;i++)
          {
              int offPos = start + i;
              if (offPos >= 0 && offPos < len)
                  result += filter[i] * in[offPos];
          }
          out[id] = result;
      }
      
      //優化1:常用卷積核常量
      __global__ void kernalConv_const(double* in,double* out, size_t len)
      {
          int id = blockIdx.x * blockDim.x + threadIdx.x;
          int start = id - FSIZE / 2;
          double result = 0.0;
          for (int i = 0;i < FSIZE;i++)
          {
              int offPos = start + i;
              if (offPos >= 0 && offPos < len)
                  result += fliterConst[i] * in[offPos];
          }
          out[id] = result;
      }
      
      //優化1:使用卷積核放常量
      //優化2:將一個線程塊中可能訪問的輸入數據放入共享內存,防止頻繁訪問全局內存
      //分析:每個共享內存變量中存放本block需要計算的數據
      __global__ void kernelConv_const_share(double* in,double* out, size_t len)
      {
          //每個block中都有此shareM數據空間,需要每個塊分別初始化自己的計算數據
          //共享內存初始化思路:在塊內,根據threadIdx判斷當前線程在塊內是否前/后raduis個線程,需要額外初始化padding數據;
          // (1)如果是索引為0 的塊,左側的padding應為0,否則應為前一個數據塊的后radius個數據
          // (2)如果是索引為最后一個塊,右側的padding應為0,否則應為后一個數據塊的前radius個數據
          //并且每個線程根據全局唯一一維id作為輸入數據索引,直接數據賦值給共享內存[threadIdx+radius]索引的空間
          __shared__ double shareM[BlockSize+FSIZE-1];
          int id = blockIdx.x * blockDim.x + threadIdx.x;
          int radius = FSIZE/2;
          // 1.先初始化padding數據
          //前raduis個線程分別初始化raduis長度的padding中的一個元素
          if(threadIdx.x < radius)
          {
              //判斷當前塊是否是一個塊
              if(blockIdx.x == 0)
                  shareM[threadIdx.x] = 0;
              else
                  //這里使用塊的臨近padding的前raduis個線程做padding初始化;也可以在非最后一個塊中用前raduis個線程做右側padding的初始化,
                  //寫法為 shareM[blockDim.x + radius + threadIdx.x] = in[(blockIdx.x+1) * blockDim.x + threadIdx]
                  shareM[threadIdx.x] = in[id-radius];
          }
          else if(threadIdx.x >= blockDim.x-radius && threadIdx.x < blockDim.x)
          {
              //判斷當前是否最后一個塊
              if(blockIdx.x == (len + blockDim.x - 1) / blockDim.x -1)
                  shareM[threadIdx.x + 2*radius] = 0;
              else
                  shareM[threadIdx.x + 2*radius] = in[id+radius];
          }
      
          // 2. 初始化非padding數據
          //初始化本線程對應輸入計算的數據到共享內存
          shareM[threadIdx.x + radius] = in[id];
      
          // 3. 線程同步
          __syncthreads();
      
          // 4. 計算
          double result = 0.0;
          for (int i = 0;i < FSIZE;i++)
          {
              int offPos = threadIdx.x + i;
              if (offPos >= 0 && offPos < len)
                  result += fliterConst[i] * shareM[offPos];
          }
          out[id] = result;
      }
      
      //串行版本
      float serial()
      {
          //host串行計算
          long start = getTime();
          serialConv(in, out, QW, filter, FSIZE);
          long end = getTime();  
      
          return end-start;
      }
      
      //GPU處理
      float gpu()
      {
         //定義gpu全局內存數據空間
         double* inD=NULL;
         double* outD=NULL;
         double* filterD=NULL;
         //這里必須要取地址 &inD, 否則開辟空間失敗
         printf("memory in err %d\n",cudaMalloc((void**)&inD, QW * sizeof(double)));
         printf("memory out err %d\n",cudaMalloc((void**)&outD, QW * sizeof(double)));
         printf("memory filter err %d\n",cudaMalloc((void**)&filterD, FSIZE* sizeof(double)));
         //拷貝數據
         cudaMemcpy(inD, in, QW * sizeof(double), cudaMemcpyHostToDevice);
         cudaMemcpy(filterD, filter, FSIZE * sizeof(double), cudaMemcpyHostToDevice);
      
         //核函數計算
         //定義blockDim
         dim3 blockDim(BlockSize,1,1);
         //grid可以使用一維
         dim3 gridDim((QW + blockDim.x - 1) / blockDim.x, 1, 1);
      
         //cuda 記錄時間,單位為 ms
         cudaEvent_t startD, endD;
         float gpuTime = 0.0;
         cudaEventCreate(&startD);
         cudaEventCreate(&endD);
         cudaEventRecord(startD, 0);
         kernalConv<<<gridDim, blockDim>>>(inD,outD, QW, filterD, FSIZE);
         cudaEventRecord(endD, 0);
         //等待事件完成
         cudaEventSynchronize(endD);
         cudaEventElapsedTime(&gpuTime, startD, endD);
         cudaEventDestroy(startD);
         cudaEventDestroy(endD);  
         
         //拷貝計算結果
         cudaMemcpy(outFD, outD, QW * sizeof(double), cudaMemcpyDeviceToHost);
      
         cudaFree(inD);
         cudaFree(outD);
         cudaFree(filterD);
         return gpuTime;
      }
      
      //對卷積核使用常量內存優化,
      float const_opt()
      {
          //定義gpu全局內存數據空間
          double* inD=NULL;
          double* outD=NULL;
          // double* filterD=NULL;
          //這里必須要取地址 &inD, 否則開辟空間失敗
          printf("memory in err %d\n",cudaMalloc((void**)&inD, QW * sizeof(double)));
          printf("memory out err %d\n",cudaMalloc((void**)&outD, QW * sizeof(double)));
          // printf("memory filter err %d\n",cudaMalloc((void**)&filterD, FSIZE* sizeof(double)));
          //拷貝數據
          cudaMemcpy(inD, in, QW * sizeof(double), cudaMemcpyHostToDevice);
          // cudaMemcpy(filterD, filter, FSIZE * sizeof(double), cudaMemcpyHostToDevice);
          //常量數據拷貝
          cudaMemcpyToSymbol(fliterConst,filter,FSIZE*sizeof(double));
       
          //核函數計算
          //定義blockDim
          dim3 blockDim(BlockSize,1,1);
          //grid可以使用一維
          dim3 gridDim((QW + blockDim.x - 1) / blockDim.x, 1, 1);
       
          //cuda 記錄時間,單位為 mse
          cudaEvent_t startD, endD;
          float gpuTime = 0.0;
          cudaEventCreate(&startD);
          cudaEventCreate(&endD);
          cudaEventRecord(startD, 0);
          kernalConv_const<<<gridDim, blockDim>>>(inD,outD, QW);
          cudaEventRecord(endD, 0);
          //等待事件完成
          cudaEventSynchronize(endD);
          cudaEventElapsedTime(&gpuTime, startD, endD);
          cudaEventDestroy(startD);
          cudaEventDestroy(endD);  
          
          //拷貝計算結果
          cudaMemcpy(outFD, outD, QW * sizeof(double), cudaMemcpyDeviceToHost);
      
          cudaFree(inD);
          cudaFree(outD);
          return gpuTime;
      }
      
      float const_shared_opt()
      {
          //定義gpu全局內存數據空間
          double* inD=NULL;
          double* outD=NULL;
          //這里必須要取地址 &inD, 否則開辟空間失敗
          printf("memory in err %d\n",cudaMalloc((void**)&inD, QW * sizeof(double)));
          printf("memory out err %d\n",cudaMalloc((void**)&outD, QW * sizeof(double)));
          //拷貝數據
          cudaMemcpy(inD, in, QW * sizeof(double), cudaMemcpyHostToDevice);
          cudaMemcpyToSymbol(fliterConst, filter, FSIZE * sizeof(double));
       
          //核函數計算
          //定義blockDim
          dim3 blockDim(BlockSize,1,1);
          //grid可以使用一維
          dim3 gridDim((QW + blockDim.x - 1) / blockDim.x, 1, 1);
       
          //cuda 記錄時間,單位為 ms
          cudaEvent_t startD, endD;
          float gpuTime = 0.0;
          cudaEventCreate(&startD);
          cudaEventCreate(&endD);
          cudaEventRecord(startD, 0);
          kernelConv_const_share<<<gridDim, blockDim>>>(inD,outD, QW);
          cudaEventRecord(endD, 0);
          //等待事件完成
          cudaEventSynchronize(endD);
          cudaEventElapsedTime(&gpuTime, startD, endD);
          cudaEventDestroy(startD);
          cudaEventDestroy(endD);  
          
          //拷貝計算結果
          cudaMemcpy(outFD, outD, QW * sizeof(double), cudaMemcpyDeviceToHost);
      
          cudaFree(inD);
          cudaFree(outD);
          return gpuTime;
      }
      
      int main()
      {
          float cpuTime = 0.0;
          float gpuTime = 0.0;
          //定義均值濾波卷積核
          for (int i = 0;i < FSIZE;i++)
              *(filter + i) = (double)1.0 / FSIZE;
          //讀取一維信號
          readFile(in, QW);
      
          //串行與GPU并行對比測試
          cpuTime = serial();
      
          //gpu并行
          #if 1
          gpuTime = gpu();
          //計算結果對比
          for (int i = 0; i< QW;i++)
          {
              if (fabs(out[i] - outFD[i]) > EP)
              {
                  printf("host[%d] %2.16f. != device[%d] %2.16f\n", i, out[i], i, outFD[i]);
                  return 1;
              }
          }
          memset(outFD,0,QW*sizeof(double));
          printf("串行與gpu計算結果一致,cpu耗時 %f ms,gpu耗時 %f ms,加速比為 %f\n",cpuTime,gpuTime,cpuTime/gpuTime);
          #endif
      
          //對卷積核使用常量內存優化
          #if 1
          gpuTime = const_opt();
          //計算結果對比
          for (int i = 0; i< QW;i++)
          {
              if (fabs(out[i] - outFD[i]) > EP)
              {
                  printf("host[%d] %2.16f. != device[%d] %2.16f\n", i, out[i], i, outFD[i]);
                  return 1;
              }
          }
          memset(outFD,0,QW*sizeof(double));
          printf("串行與const_opt計算結果一致,cpu耗時 %f ms,gpu耗時 %f ms,加速比為 %f\n",cpuTime,gpuTime,cpuTime/gpuTime);
          #endif
      
          //1. 卷積核使使用常量內存
          //2. 參與block計算輸入數據放在共享內存
          #if 1
          gpuTime = const_shared_opt();
          //計算結果對比
          for (int i = 0; i< QW;i++)
          {
              if (fabs(out[i] - outFD[i]) > EP)
              {
                  printf("host[%d] %2.16f. != device[%d] %2.16f\n", i, out[i], i, outFD[i]);
                  return 1;
              }
          }
          printf("串行與const_shared_opt計算結果一致,cpu耗時 %f ms,gpu耗時 %f ms,加速比為 %f\n",cpuTime,gpuTime,cpuTime/gpuTime);
          #endif
      
          //保存計算結果
          saveFile(out, QW);
          //釋放空間
          free(in);
          free(out);
          free(outFD);
          free(filter);
          return 0;
      }
      

      3. 測試結果

      項目 耗時(ms) 加速比
      串行 215
      gpu 4.3 49.96
      gpu + 常量內存 1.96 109
      gpu + 常量內存 + 共享內存 1.54 139.6

      4. 結果分析

      延遲分析

      (1) 使用常量內存能顯著減少訪存延遲;

      (2) 使用共享內存也能極大的提升訪存延遲。

      訪存次數分析

      數據長度:len,卷積核長度: 2*radius+1 = FilterSize;

      最初的base版本:

      單個內部線程塊的訪存次數(包含卷積核和計算輸入數據)為: blockDim.x * (2FilterSize);
      單個邊緣線程塊的訪存次數為: 原次數-單邊padding減少次數( 項數
      (最多減少訪問次數+最少減少訪問次數)/2 * 2[卷積核和計算數據都減少,加倍] ):
      blockDim.x * (2*FilterSize) - radius * (radius+1)/2 * 2;

      卷積核使用常量內存存儲:

      單個內部線程塊的訪存次數為: blockDim.x * FilterSize;
      單個邊緣線程塊的訪存次數為: blockDim * FilterSize - raduis * (radius + 1)/2;

      卷積核使用常量內存,輸入計算數據使用共享內存時:

      單個內部線程塊的訪存次數為: blockDim.x + 2 * radius;
      單個邊緣線程塊的訪存次數為: blockDim.x + radius

      使用常量內存和常量內存+共享內存訪存對比:

      單個內部線程塊訪存對比:(blockDim.x * FilterSize)/(blockDim.x + 2 * radius)
      單個邊緣線程塊的訪存對比:(blockDim * FilterSize - raduis * (radius + 1)/2)/(blockDim.x + radius)

      假設 blickDim.x遠大于FilterSize,可以簡化為:

      單個內部線程塊訪存對比:(blockDim.x * FilterSize)/blockDim.x = FilterSize;
      單個邊緣線程塊的訪存對比:(blockDim * FilterSize)/blockDim.x = FilterSize;

      訪存加速結論:對輸入計算數據使用共享內存,讓線程塊內的所有線程共享訪問,輸入數據的訪存次數變為原訪問次數的 1/FilterSize.

      posted @ 2024-12-31 18:48  安洛8  閱讀(140)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 一区二区三区国产综合在线| 一区二区丝袜美腿视频| 前郭尔| 少妇人妻精品一区二区| 日韩av一区二区三区不卡| 欧美一区二区三区成人久久片| 亚洲国产成人资源在线| 日本国产精品第一页久久| 日韩深夜免费在线观看| 久9re热视频这里只有精品免费| 亚洲精品tv久久久久久久久久| 在线中文字幕国产精品| WWW丫丫国产成人精品| 国产微拍一区二区三区四区| 国产精成人品日日拍夜夜| 不卡一区二区国产在线| 成人永久免费A∨一级在线播放| 日韩欧美视频一区二区三区| 成人福利国产午夜AV免费不卡在线 | 美女一区二区三区亚洲麻豆| 国产激情av一区二区三区| 精品无码国产自产拍在线观看| 国产一区在线观看不卡| 国产欧美精品一区二区三区四区| 亚洲一区二区国产av| 看免费的无码区特aa毛片| 无码国产欧美一区二区三区不卡 | 国产精品一区中文字幕| AV无码免费不卡在线观看| 久久人与动人物a级毛片 | 综合无码一区二区三区| 香蕉EEWW99国产精选免费| 亚洲情色av一区二区| 天天做天天爱夜夜爽导航| 国产va免费精品观看| 亚洲中文字幕日产无码成人片| 一本色道婷婷久久欧美| 土默特左旗| 亚洲国产成人无码影片在线播放| 色综合视频一区二区三区| 免费人成黄页在线观看国产|