借一栗子讲解基于C的CUDA并行计算

Love The Way You Lie 2022-11-14 11:49 101阅读 0赞

一、C语言接口回顾

代码1

  1. // ConsoleApplication6.cpp : 定义控制台应用程序的入口点。
  2. //
  3. #include "stdafx.h"
  4. #include <iostream>
  5. using namespace std;
  6. //自定义数据类型 数据对齐
  7. typedef struct student
  8. {
  9. char label[1]; //1个字节
  10. int number; //4个字节
  11. float score; //4个字节
  12. }stu;
  13. int _tmain(int argc, _TCHAR* argv[])
  14. {
  15. //在cpu-memory模式下面 数据是需要对齐的 因为这样cpu和memory的内存交互速度是最快的
  16. //在硬件设计的时候 就已经把这种属性 注入到硬件的平台
  17. //但是在gpu平台上面 对于数据对齐的处理要求会更严格
  18. //因为并行计算 需要数据的提供延时更小 所以在不是4字节的类型里面 会自动进行填充
  19. //也就是说在gpu平台上面 这个student结构体12字节
  20. std::cout << "sizeof(stu):" << sizeof(stu) << endl;
  21. // printf(sizeof(stu));
  22. //void*类型作为一个万能类型 专门给强制类型转换预留的接口 内核驱动里面和回掉函数里面 会有大量应用
  23. // malloc free memset memcpy
  24. char *p = (char *)malloc(100 * sizeof(char));
  25. //申请的空间里面的值全部赋值成0
  26. memset(p, 0, 100);
  27. char p_str[50] = "I love C++ program!!!";
  28. //char *pstr = "I love C program!!!";
  29. //把内存里面的值进行拷贝
  30. memcpy(p, p_str, strlen(p_str));
  31. //格式化输出
  32. printf("%s", p);
  33. //申请的内存空间需要释放 告诉编译器现在这段空间我不需要了 编译器进而通知系统 这段空间我现在不需要了 最后系统把这段空间重新利用
  34. free(p);
  35. system("pause");
  36. return 0;
  37. }

总结

  1. c/c++写一段代码流程:
    1.申请内存 malloc / new
    2.内存初始化 memset
    3.处理数据(此处是复制数据,memcpy)
    4.输出 (printf)
    5.释放内存 free/ delete
  2. 自定义数据,数据类型对齐
    在cpu-memory模式下面,数据是需要对齐的,因为这样cpu和memory的内存交互速度是最快的。
    在硬件设计的时候,就已经把这种属性,注入到硬件的平台。
    但是在gpu平台上面,对于数据对齐的处理要求会更严格。
    因为并行计算,需要数据的提供延时更小,所以在不是4字节的类型里面,会自动进行填充 。
    void*类型作为一个万能类型,专门给强制类型转换预留的接口,内核驱动里面和回掉函数里面会有大量应用。

二、 cuda编程基础概念

基础概念
主机:cpu和内存
设备:gpu和显存
API:
warp: thread
访问速度不同
变量类型限定符:device shared constant
函数类型限定符号 global 在cpu上定义,在gpu上显存上执行
device gpu上执行
host 主机上执行 cpu

thread:线程 cpu架构基本一致 同步
多少个流就可以创建多少个线程
block: 多个线程
grid: 多个block
SIMT:单指令多线程
内置变量:threadid blockid blockdim gridid griddim
显存带宽犹如高速公路的车道,优化的方法是尽可能接近理论的带宽。

代码2

  1. /***************************************************************************
  2. * first_cuda.cu
  3. *1.将数据从主机内存数据复制到设备显存
  4. *2.写好核函数
  5. *3.CUDA编译器执行核函数 在GPU上完成计算操作
  6. *4.把显存数据复制到主机内存
  7. *5.释放显存空间
  8. /***************************************************************************/
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. //CUDA RunTime API
  12. #include <cuda_runtime.h>
  13. #define DATA_SIZE 1048576
  14. int data[DATA_SIZE];
  15. //产生大量0-9之间的随机数
  16. // 指针不带内存大小,所以需要传入一个size,指明大小
  17. void GenerateNumbers(int *number, int size)
  18. {
  19. for (int i = 0; i < size; i++) {
  20. number[i] = rand() % 10;
  21. }
  22. }
  23. //CUDA 初始化
  24. bool InitCUDA()
  25. {
  26. int count;
  27. //取得支持Cuda的装置的数目
  28. cudaGetDeviceCount(&count);
  29. if (count == 0) {
  30. fprintf(stderr, "There is no device.\n");
  31. return false;
  32. }
  33. int i;
  34. for (i = 0; i < count; i++) {
  35. cudaDeviceProp prop;
  36. if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
  37. if (prop.major >= 1) {
  38. break;
  39. }
  40. }
  41. }
  42. if (i == count) {
  43. fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
  44. return false;
  45. }
  46. cudaSetDevice(i);
  47. return true;
  48. }
  49. // __global__ 函数 (GPU上执行) 计算立方和
  50. //核函数是不可以有返回值类型的
  51. __global__ static void sumOfSquares(int *num, int* result)
  52. {
  53. int sum = 0;
  54. int i;
  55. for (i = 0; i < DATA_SIZE; i++) {
  56. sum += num[i] * num[i] * num[i];
  57. }
  58. *result = sum;
  59. }
  60. int main()
  61. {
  62. //CUDA 初始化
  63. if (!InitCUDA()) {
  64. return 0;
  65. }
  66. //生成随机数
  67. GenerateNumbers(data, DATA_SIZE);
  68. /*把数据复制到显卡内存中*/
  69. int* gpudata, *result;
  70. //cudaMalloc 取得一块显卡内存 ( 其中result用来存储计算结果 )
  71. cudaMalloc((void**)&gpudata, sizeof(int)* DATA_SIZE);
  72. cudaMalloc((void**)&result, sizeof(int));
  73. //cudaMemcpy 将产生的随机数复制到显卡内存中
  74. //cudaMemcpyHostToDevice - 从内存复制到显卡内存
  75. //cudaMemcpyDeviceToHost - 从显卡内存复制到内存
  76. cudaMemcpy(gpudata, data, sizeof(int)* DATA_SIZE, cudaMemcpyHostToDevice);
  77. // 在CUDA 中执行函数 语法:函数名称<<<block 数目, thread 数目, shared memory 大小>>>(参数...);
  78. sumOfSquares << <1, 1, 0 >> >(gpudata, result);
  79. /*把结果从显示芯片复制回主内存*/
  80. int sum;
  81. //cudaMemcpy 将结果从显存中复制回内存
  82. cudaMemcpy(&sum, result, sizeof(int), cudaMemcpyDeviceToHost);
  83. //Free
  84. cudaFree(gpudata);
  85. cudaFree(result);
  86. printf("GPUsum: %d \n", sum);
  87. sum = 0;
  88. for (int i = 0; i < DATA_SIZE; i++) {
  89. sum += data[i] * data[i] * data[i];
  90. }
  91. printf("CPUsum: %d \n", sum);
  92. return 0;
  93. }

总结cuda编程的流程

  1. 申请显存空间 cudaMalloc
  2. 将数据从主机内存数据复制到设备显存
  3. 写好核函数
  4. CUDA编译器执行核函数 在GPU上完成计算操作
  5. 把显存数据复制到主机内存
  6. 释放显存空间

API

  1. 获取 CUDA 设备数:
    函数原型:

    cudaError_t cudaGetDeviceCount( int* count )

可以通过 cudaGetDeviceCount 函数获取 CUDA 的设备数,实例如上InitCUDA()函数通过引用传递 count 值,获取当前支持的 CUDA 设备数,即返回具有计算能力的设备的数量。
以 *count 形式返回可用于执行的计算能力大于等于 1.0 的设备数量。如果不存在此类设备,将返回 1
cudaSuccess,注意,如果之前是异步启动,该函数可能返回错误码。

  1. 获取 CUDA 设备属性
    函数原型:

    cudaError_t cudaGetDeviceProperties( struct cudaDeviceProp* prop,int dev )

可以通过 cudaGetDeviceProperties 函数获取 CUDA 设备的属性,具体用法
函数通过引用传递 prop 关于属性的结构体,并且列出主设备号大于 1 的设备属性,其中设备属性通过函数 printDeviceProp 打印。
以*prop形式返回设备dev的属性。
返回值:cudaSuccess、cudaErrorInvalidDevice,注,如果之前是异步启动,该函数可能返回错误码。

  1. 设置 CUDA 设备
    通过函数 cudaSetDevice 就可以设置 CUDA 设备了,具体用法

    // set cuda device
    cudaSetDevice(i);

原型:

  1. cudaError_t cudaSetDevice(int dev)

将dev记录为活动主线程将执行设备码的设备。
cudaSuccess、cudaErrorInvalidDevice,注,如果之前是异步启动,该函数可能返回错误码。
cudaDeviceProp 结构定义如下:

  1. struct cudaDeviceProp {
  2. char name [256];
  3. size_t totalGlobalMem;
  4. size_t sharedMemPerBlock;
  5. int regsPerBlock;
  6. int warpSize;
  7. size_t memPitch;
  8. int maxThreadsPerBlock;
  9. int maxThreadsDim [3];
  10. int maxGridSize [3];
  11. size_t totalConstMem;
  12. int major;
  13. int minor;
  14. int clockRate;
  15. size_t textureAlignment;
  16. int deviceOverlap;
  17. int multiProcessorCount;
  18. }

CUDA 初始化完整代码函数为InitCUDA()
cudaDeviceProp 结构中的各个变量意义如下
name:用于标识设备的ASCII字符串;
totalGlobalMem:设备上可用的全局存储器的总量,以字节为单位;
sharedMemPerBlock:线程块可以使用的共享存储器的最大值,以字节为单位;多处理器上的所有线程块可以同时共享这些存储器;
regsPerBlock:线程块可以使用的32位寄存器的最大值;多处理器上的所有线程块可以同时共享这些寄存器;
warpSize:按线程计算的warp块大小;
memPitch:允许通过cudaMallocPitch()为包含存储器区域的存储器复制函数分配的最大间距(pitch),以字节为单位;
maxThreadsPerBlock:每个块中的最大线程数
maxThreadsDim[3]:块各个维度的最大值:
maxGridSize[3]:网格各个维度的最大值;
totalConstMem:设备上可用的不变存储器总量,以字节为单位;
major,minor:定义设备计算能力的主要修订号和次要修订号;
clockRate:以千赫为单位的时钟频率;
textureAlignment:对齐要求;与textureAlignment字节对齐的纹理基址无需对纹理取样应用偏移;
deviceOverlap:如果设备可在主机和设备之间并发复制存储器,同时又能执行内核,则此值为 1;否则此值为 0;
multiProcessorCount:设备上多处理器的数量。

三、 CUDA程序优化和评估的方式

代码3:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. //CUDA RunTime API
  5. #include <cuda_runtime.h>
  6. //1024 * 1024 1M
  7. #define DATA_SIZE 1048576
  8. int data[DATA_SIZE];
  9. float clockRate = 1.0;
  10. //产生大量0-9之间的随机数
  11. void GenerateNumbers(int *number, int size)
  12. {
  13. for (int i = 0; i < size; i++) {
  14. number[i] = rand() % 10;
  15. }
  16. }
  17. //打印设备属性
  18. void printDeviceProp(const cudaDeviceProp &prop)
  19. {
  20. printf("Device Name : %s.\n", prop.name);
  21. printf("totalGlobalMem : %d.\n", prop.totalGlobalMem);
  22. printf("sharedMemPerBlock : %d.\n", prop.sharedMemPerBlock);
  23. printf("regsPerBlock : %d.\n", prop.regsPerBlock);
  24. printf("warpSize : %d.\n", prop.warpSize);
  25. printf("memPitch : %d.\n", prop.memPitch);
  26. printf("maxThreadsPerBlock : %d.\n", prop.maxThreadsPerBlock);
  27. printf("maxThreadsDim[0 - 2] : %d %d %d.\n", prop.maxThreadsDim[0], prop.maxThreadsDim[1], prop.maxThreadsDim[2]);
  28. printf("maxGridSize[0 - 2] : %d %d %d.\n", prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2]);
  29. printf("totalConstMem : %d.\n", prop.totalConstMem);
  30. printf("major.minor : %d.%d.\n", prop.major, prop.minor);
  31. printf("clockRate : %d.\n", prop.clockRate);
  32. printf("textureAlignment : %d.\n", prop.textureAlignment);
  33. printf("deviceOverlap : %d.\n", prop.deviceOverlap);
  34. printf("multiProcessorCount : %d.\n", prop.multiProcessorCount);
  35. }
  36. //CUDA 初始化
  37. bool InitCUDA()
  38. {
  39. int count;
  40. //取得支持Cuda的装置的数目
  41. cudaGetDeviceCount(&count);
  42. if (count == 0) {
  43. fprintf(stderr, "There is no device.\n");
  44. return false;
  45. }
  46. int i;
  47. //取得显卡属性
  48. for (i = 0; i < count; i++) {
  49. cudaDeviceProp prop;
  50. cudaGetDeviceProperties(&prop, i);
  51. //打印gpu设备信息
  52. printDeviceProp(prop);
  53. //获得显卡的始终频率
  54. clockRate = prop.clockRate;
  55. if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
  56. if (prop.major >= 1) {
  57. break;
  58. }
  59. }
  60. }
  61. if (i == count) {
  62. fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
  63. return false;
  64. }
  65. cudaSetDevice(i);
  66. return true;
  67. }
  68. // __global__ 函数 (GPU上执行) 计算立方和
  69. __global__ static void sumOfSquares(int *num, int* result, clock_t* time)
  70. {
  71. int sum = 0;
  72. int i;
  73. clock_t start = clock();
  74. for (i = 0; i < DATA_SIZE; i++) {
  75. sum += num[i] * num[i] * num[i];
  76. }
  77. *result = sum;
  78. *time = clock() - start;
  79. }
  80. int main()
  81. {
  82. //CUDA 初始化
  83. if (!InitCUDA()) {
  84. return 0;
  85. }
  86. //生成随机数(初始化全局的数组,指针不指定申请内存大小)
  87. GenerateNumbers(data, DATA_SIZE);
  88. /*把数据复制到显卡内存中*/
  89. int* gpudata, *result;
  90. clock_t* time;
  91. //cudaMalloc 取得一块显卡内存 ( 其中result用来存储计算结果,time用来存储运行时间 )
  92. cudaMalloc((void**)&gpudata, sizeof(int)* DATA_SIZE);
  93. cudaMalloc((void**)&result, sizeof(int));
  94. cudaMalloc((void**)&time, sizeof(clock_t));
  95. //cudaMemcpy 将产生的随机数复制到显卡内存中
  96. //cudaMemcpyHostToDevice - 从内存复制到显卡内存
  97. //cudaMemcpyDeviceToHost - 从显卡内存复制到内存
  98. cudaMemcpy(gpudata, data, sizeof(int)* DATA_SIZE, cudaMemcpyHostToDevice);
  99. // 在CUDA 中执行函数 语法:函数名称<<<block 数目, thread 数目, shared memory 大小>>>(参数...);
  100. sumOfSquares << <1, 1, 0 >> >(gpudata, result, time);
  101. /*把结果从显示芯片复制回主内存*/
  102. int sum;
  103. clock_t time_used;
  104. //cudaMemcpy 将结果从显存中复制回内存,结果在cpu上显示出来
  105. cudaMemcpy(&sum, result, sizeof(int), cudaMemcpyDeviceToHost);
  106. cudaMemcpy(&time_used, time, sizeof(clock_t), cudaMemcpyDeviceToHost);
  107. //Free
  108. cudaFree(gpudata);
  109. cudaFree(result);
  110. cudaFree(time);
  111. printf("GPUsum: %d time_clock: %d time: %fs\n", sum, time_used, ((float)time_used / (clockRate * 1000)));
  112. sum = 0;
  113. for (int i = 0; i < DATA_SIZE; i++) {
  114. //循环1M的数据
  115. sum += data[i] * data[i] * data[i];
  116. }
  117. printf("CPUsum: %d \n", sum);
  118. return 0;
  119. }

以上代码分析以及优化方式
1M的数据(1024*1024) int类型是4个字节,即4byte数据 ,一共是的数据量就是4M
带宽:4MB/1.026178 = 3.89MB/s (4MB的数据,用时是1.026178s,计算得出3.89MB/s)
我显卡的带宽是14Gb/s左右 也就是说 完全没有实现并行计算的威力
优化

  1. 一定要先从显存带宽开始
  2. 确定任务中并行和串行的算法
  3. 需要两层线程并行的内核函数,每个SM上面至少有6个warp和2个block
  4. 共享内存 shared memory

nvcc 编译代码
nvcc 是 CUDA 的编译工具,它可以将 .cu 文件解析出在 GPU 和 host 上执行的部分,也就是说,它会帮忙把 GPU 上执行和主机上执行的代码区分开来,不许要我们手动去做了。在 GPU 执行的部分会通过 NVIDIA 提供的 编译器编译成中介码,主机执行的部分则调用 gcc 编译。

  1. nvcc -o first_cuda first_cuda.cu

通过上述编译,生成可执行文件 first_cuda

四、 thread多线程概念的引入

cuda中GPU 的架构。它是由 grid 组成,每个 grid 又可以由 block 组成,而每个 block 又可以细分为 thread。所以,线程是我们处理的最小的单元。
接下来的例子通过修改前一个例子,把数组分割成若干个组(每个组由一个线程实现),每个组计算出一个和,然后在 CPU 中将分组的这几个和加在一起,得到最终的结果。这种思想叫做归约 。其实和分治思想差不多,就是先将大规模问题分解为小规模的问题,最后这些小规模问题整合得到最终解。
由于我的 GPU 支持的块内最大的线程数是 256个,即 cudaGetDeviceProperties 中的 maxThreadsPerBlock 属性。如何获取这个属性。
我们使用 256 个线程来实现并行加速。

代码4:thread多线程概念的引入:用时0.0244065s

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. //CUDA RunTime API
  5. #include <cuda_runtime.h>
  6. //1M
  7. #define DATA_SIZE 1048576
  8. #define THREAD_NUM 256 //256线程
  9. int data[DATA_SIZE];
  10. int clockRate = 1.0;
  11. //产生大量0-9之间的随机数
  12. void GenerateNumbers(int *number, int size)
  13. {
  14. for (int i = 0; i < size; i++) {
  15. number[i] = rand() % 10;
  16. }
  17. }
  18. //打印设备信息
  19. void printDeviceProp(const cudaDeviceProp &prop)
  20. {
  21. printf("Device Name : %s.\n", prop.name);
  22. printf("totalGlobalMem : %d.\n", prop.totalGlobalMem);
  23. printf("sharedMemPerBlock : %d.\n", prop.sharedMemPerBlock);
  24. printf("regsPerBlock : %d.\n", prop.regsPerBlock);
  25. printf("warpSize : %d.\n", prop.warpSize);
  26. printf("memPitch : %d.\n", prop.memPitch);
  27. printf("maxThreadsPerBlock : %d.\n", prop.maxThreadsPerBlock);
  28. printf("maxThreadsDim[0 - 2] : %d %d %d.\n", prop.maxThreadsDim[0], prop.maxThreadsDim[1], prop.maxThreadsDim[2]);
  29. printf("maxGridSize[0 - 2] : %d %d %d.\n", prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2]);
  30. printf("totalConstMem : %d.\n", prop.totalConstMem);
  31. printf("major.minor : %d.%d.\n", prop.major, prop.minor);
  32. printf("clockRate : %d.\n", prop.clockRate);
  33. printf("textureAlignment : %d.\n", prop.textureAlignment);
  34. printf("deviceOverlap : %d.\n", prop.deviceOverlap);
  35. printf("multiProcessorCount : %d.\n", prop.multiProcessorCount);
  36. }
  37. //CUDA 初始化
  38. bool InitCUDA()
  39. {
  40. int count;
  41. //取得支持Cuda的装置的数目
  42. cudaGetDeviceCount(&count);
  43. if (count == 0) {
  44. fprintf(stderr, "There is no device.\n");
  45. return false;
  46. }
  47. int i;
  48. for (i = 0; i < count; i++) {
  49. cudaDeviceProp prop;
  50. cudaGetDeviceProperties(&prop, i);
  51. //打印设备信息
  52. printDeviceProp(prop);
  53. //获得显卡的时钟频率
  54. clockRate = prop.clockRate;
  55. if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
  56. if (prop.major >= 1) {
  57. break;
  58. }
  59. }
  60. }
  61. if (i == count) {
  62. fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
  63. return false;
  64. }
  65. cudaSetDevice(i);
  66. return true;
  67. }
  68. // __global__ 函数 (GPU上执行) 计算立方和
  69. __global__ static void sumOfSquares(int *num, int* result, clock_t* time)
  70. {
  71. //表示目前的 thread 是第几个 thread(由 0 开始计算)
  72. const int tid = threadIdx.x; //tid相当于一个向量,大小为256
  73. //计算每个线程需要完成的量
  74. const int size = DATA_SIZE / THREAD_NUM;
  75. int sum = 0;
  76. int i;
  77. //记录运算开始的时间
  78. clock_t start;
  79. //只在 thread 0(即 threadIdx.x = 0 的时候)进行记录
  80. if (tid == 0) start = clock();
  81. for (i = tid * size; i < (tid + 1) * size; i++) {
  82. sum += num[i] * num[i] * num[i];
  83. }
  84. result[tid] = sum;
  85. //计算时间的动作,只在 thread 0(即 threadIdx.x = 0 的时候)进行
  86. if (tid == 0) *time = clock() - start;
  87. }
  88. int main()
  89. {
  90. //CUDA 初始化
  91. if (!InitCUDA()) {
  92. return 0;
  93. }
  94. //生成随机数
  95. GenerateNumbers(data, DATA_SIZE);
  96. /*把数据复制到显卡内存中*/
  97. int* gpudata, *result;
  98. clock_t* time;
  99. //cudaMalloc 取得一块显卡内存 ( 其中result用来存储计算结果,time用来存储运行时间 )
  100. cudaMalloc((void**)&gpudata, sizeof(int)* DATA_SIZE);
  101. cudaMalloc((void**)&result, sizeof(int)*THREAD_NUM);
  102. cudaMalloc((void**)&time, sizeof(clock_t));
  103. //cudaMemcpy 将产生的随机数复制到显卡内存中
  104. //cudaMemcpyHostToDevice - 从内存复制到显卡内存
  105. //cudaMemcpyDeviceToHost - 从显卡内存复制到内存
  106. cudaMemcpy(gpudata, data, sizeof(int)* DATA_SIZE, cudaMemcpyHostToDevice);
  107. // 在CUDA 中执行函数 语法:函数名称<<<block 数目, thread 数目, shared memory 大小>>>(参数...);
  108. sumOfSquares << < 1, THREAD_NUM, 0 >> >(gpudata, result, time);
  109. /*把结果从显示芯片复制回主内存*/
  110. int sum[THREAD_NUM];
  111. clock_t time_use;
  112. //cudaMemcpy 将结果从显存中复制回内存
  113. cudaMemcpy(&sum, result, sizeof(int)* THREAD_NUM, cudaMemcpyDeviceToHost);
  114. cudaMemcpy(&time_use, time, sizeof(clock_t), cudaMemcpyDeviceToHost);
  115. //Free
  116. cudaFree(gpudata);
  117. cudaFree(result);
  118. cudaFree(time);
  119. int final_sum = 0;
  120. for (int i = 0; i < THREAD_NUM; i++) {
  121. final_sum += sum[i];
  122. }
  123. printf("GPUsum: %d time_clock: %d time: %fs\n", final_sum, time_use, ((float)time_use / (clockRate * 1000)));
  124. final_sum = 0;
  125. for (int i = 0; i < DATA_SIZE; i++) {
  126. final_sum += data[i] * data[i] * data[i];
  127. }
  128. printf("CPUsum: %d \n", final_sum);
  129. return 0;
  130. }
  131. //创建了256个线程帮我们并行计算 我们需要提前安排好每个线程计算的数据 防止线程同步的问题

重点代码分析

  1. __global__ static void sumOfSquares(int *num, int* result, clock_t* time)
  2. {
  3. //表示目前的 thread 是第几个 thread(由 0 开始计算)
  4. const int tid = threadIdx.x; //tid相当于一个向量,大小为256
  5. //计算每个线程需要完成的量
  6. const int size = DATA_SIZE / THREAD_NUM;
  7. int sum = 0;
  8. int i;
  9. //记录运算开始的时间
  10. clock_t start;
  11. //只在 thread 0(即 threadIdx.x = 0 的时候)进行记录
  12. if (tid == 0) start = clock();
  13. for (i = tid * size; i < (tid + 1) * size; i++) {
  14. sum += num[i] * num[i] * num[i];
  15. }
  16. result[tid] = sum;
  17. //计算时间的动作,只在 thread 0(即 threadIdx.x = 0 的时候)进行
  18. if (tid == 0) *time = clock() - start;
  19. }
  20. int main()
  21. {
  22. //C

代码5:内存连续存储:用时0.0053375s
线程:0,1,2…,255, 0,1,2…255

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. //CUDA RunTime API
  5. #include <cuda_runtime.h>
  6. //1M
  7. #define DATA_SIZE 1048576
  8. #define THREAD_NUM 256
  9. int data[DATA_SIZE];
  10. int clockRate = 1.0;
  11. //产生大量0-9之间的随机数
  12. void GenerateNumbers(int *number, int size)
  13. {
  14. for (int i = 0; i < size; i++) {
  15. number[i] = rand() % 10;
  16. }
  17. }
  18. //打印设备信息
  19. void printDeviceProp(const cudaDeviceProp &prop)
  20. {
  21. printf("Device Name : %s.\n", prop.name);
  22. printf("totalGlobalMem : %d.\n", prop.totalGlobalMem);
  23. printf("sharedMemPerBlock : %d.\n", prop.sharedMemPerBlock);
  24. printf("regsPerBlock : %d.\n", prop.regsPerBlock);
  25. printf("warpSize : %d.\n", prop.warpSize);
  26. printf("memPitch : %d.\n", prop.memPitch);
  27. printf("maxThreadsPerBlock : %d.\n", prop.maxThreadsPerBlock);
  28. printf("maxThreadsDim[0 - 2] : %d %d %d.\n", prop.maxThreadsDim[0], prop.maxThreadsDim[1], prop.maxThreadsDim[2]);
  29. printf("maxGridSize[0 - 2] : %d %d %d.\n", prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2]);
  30. printf("totalConstMem : %d.\n", prop.totalConstMem);
  31. printf("major.minor : %d.%d.\n", prop.major, prop.minor);
  32. printf("clockRate : %d.\n", prop.clockRate);
  33. printf("textureAlignment : %d.\n", prop.textureAlignment);
  34. printf("deviceOverlap : %d.\n", prop.deviceOverlap);
  35. printf("multiProcessorCount : %d.\n", prop.multiProcessorCount);
  36. }
  37. //CUDA 初始化
  38. bool InitCUDA()
  39. {
  40. int count;
  41. //取得支持Cuda的装置的数目
  42. cudaGetDeviceCount(&count);
  43. if (count == 0) {
  44. fprintf(stderr, "There is no device.\n");
  45. return false;
  46. }
  47. int i;
  48. for (i = 0; i < count; i++) {
  49. cudaDeviceProp prop;
  50. cudaGetDeviceProperties(&prop, i);
  51. //打印设备信息
  52. printDeviceProp(prop);
  53. //获得显卡的时钟频率
  54. clockRate = prop.clockRate;
  55. if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
  56. if (prop.major >= 1) {
  57. break;
  58. }
  59. }
  60. }
  61. if (i == count) {
  62. fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
  63. return false;
  64. }
  65. cudaSetDevice(i);
  66. return true;
  67. }
  68. // __global__ 函数 (GPU上执行) 计算立方和
  69. __global__ static void sumOfSquares(int *num, int* result, clock_t* time)
  70. {
  71. //表示目前的 thread 是第几个 thread(由 0 开始计算)
  72. const int tid = threadIdx.x;
  73. int sum = 0;
  74. int i;
  75. //记录运算开始的时间
  76. clock_t start;
  77. //只在 thread 0(即 threadIdx.x = 0 的时候)进行记录
  78. if (tid == 0) start = clock();
  79. //改为连续存取(thread 0 读取第一个数字,thread 1 读取第二个数字 …)
  80. for (i = tid; i < DATA_SIZE; i += THREAD_NUM) {
  81. sum += num[i] * num[i] * num[i];
  82. }
  83. result[tid] = sum;
  84. //计算时间的动作,只在 thread 0(即 threadIdx.x = 0 的时候)进行
  85. if (tid == 0) *time = clock() - start;
  86. }
  87. int main()
  88. {
  89. //CUDA 初始化
  90. if (!InitCUDA()) {
  91. return 0;
  92. }
  93. //生成随机数
  94. GenerateNumbers(data, DATA_SIZE);
  95. /*把数据复制到显卡内存中*/
  96. int* gpudata, *result;
  97. clock_t* time;
  98. //cudaMalloc 取得一块显卡内存 ( 其中result用来存储计算结果,time用来存储运行时间 )
  99. cudaMalloc((void**)&gpudata, sizeof(int)* DATA_SIZE);
  100. cudaMalloc((void**)&result, sizeof(int)*THREAD_NUM);
  101. cudaMalloc((void**)&time, sizeof(clock_t));
  102. //cudaMemcpy 将产生的随机数复制到显卡内存中
  103. //cudaMemcpyHostToDevice - 从内存复制到显卡内存
  104. //cudaMemcpyDeviceToHost - 从显卡内存复制到内存
  105. cudaMemcpy(gpudata, data, sizeof(int)* DATA_SIZE, cudaMemcpyHostToDevice);
  106. // 在CUDA 中执行函数 语法:函数名称<<<block 数目, thread 数目, shared memory 大小>>>(参数...);
  107. sumOfSquares << < 1, THREAD_NUM, 0 >> >(gpudata, result, time);
  108. /*把结果从显示芯片复制回主内存*/
  109. int sum[THREAD_NUM];
  110. clock_t time_use;
  111. //cudaMemcpy 将结果从显存中复制回内存
  112. cudaMemcpy(&sum, result, sizeof(int)* THREAD_NUM, cudaMemcpyDeviceToHost);
  113. cudaMemcpy(&time_use, time, sizeof(clock_t), cudaMemcpyDeviceToHost);
  114. //Free
  115. cudaFree(gpudata);
  116. cudaFree(result);
  117. cudaFree(time);
  118. int final_sum = 0;
  119. for (int i = 0; i < THREAD_NUM; i++) {
  120. final_sum += sum[i];
  121. }
  122. printf("GPUsum: %d time_clock: %d time: %f\n", final_sum, time_use, ((float)time_use / (clockRate * 1000)));
  123. final_sum = 0;
  124. for (int i = 0; i < DATA_SIZE; i++) {
  125. final_sum += data[i] * data[i] * data[i];
  126. }
  127. printf("CPUsum: %d \n", final_sum);
  128. return 0;
  129. }
  130. //我们的程序需要尽可能连续操作内存,减少内存存取方面的时间浪费

五、block线程块概念

代码6:block线程块概念引入:用时:0.002125s
在这里插入图片描述

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. //CUDA RunTime API
  5. #include <cuda_runtime.h>
  6. //1M
  7. #define DATA_SIZE 1048576 //1024*1024
  8. #define THREAD_NUM 256
  9. #define BLOCK_NUM 32 // block
  10. int clockRate = 1.0;
  11. int data[DATA_SIZE];
  12. //产生大量0-9之间的随机数
  13. void GenerateNumbers(int *number, int size)
  14. {
  15. for (int i = 0; i < size; i++) {
  16. number[i] = rand() % 10;
  17. }
  18. }
  19. //打印设备信息
  20. void printDeviceProp(const cudaDeviceProp &prop)
  21. {
  22. printf("Device Name : %s.\n", prop.name);
  23. printf("totalGlobalMem : %d.\n", prop.totalGlobalMem);
  24. printf("sharedMemPerBlock : %d.\n", prop.sharedMemPerBlock);
  25. printf("regsPerBlock : %d.\n", prop.regsPerBlock);
  26. printf("warpSize : %d.\n", prop.warpSize);
  27. printf("memPitch : %d.\n", prop.memPitch);
  28. printf("maxThreadsPerBlock : %d.\n", prop.maxThreadsPerBlock);
  29. printf("maxThreadsDim[0 - 2] : %d %d %d.\n", prop.maxThreadsDim[0], prop.maxThreadsDim[1], prop.maxThreadsDim[2]);
  30. printf("maxGridSize[0 - 2] : %d %d %d.\n", prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2]);
  31. printf("totalConstMem : %d.\n", prop.totalConstMem);
  32. printf("major.minor : %d.%d.\n", prop.major, prop.minor);
  33. printf("clockRate : %d.\n", prop.clockRate);
  34. printf("textureAlignment : %d.\n", prop.textureAlignment);
  35. printf("deviceOverlap : %d.\n", prop.deviceOverlap);
  36. printf("multiProcessorCount : %d.\n", prop.multiProcessorCount);
  37. }
  38. //CUDA 初始化
  39. bool InitCUDA()
  40. {
  41. int count;
  42. //取得支持Cuda的装置的数目
  43. cudaGetDeviceCount(&count);
  44. if (count == 0) {
  45. fprintf(stderr, "There is no device.\n");
  46. return false;
  47. }
  48. int i;
  49. for (i = 0; i < count; i++) {
  50. cudaDeviceProp prop;
  51. cudaGetDeviceProperties(&prop, i);
  52. //打印设备信息
  53. printDeviceProp(prop);
  54. //获得显卡的时钟频率
  55. clockRate = prop.clockRate;
  56. if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
  57. if (prop.major >= 1) {
  58. break;
  59. }
  60. }
  61. }
  62. if (i == count) {
  63. fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
  64. return false;
  65. }
  66. cudaSetDevice(i);
  67. return true;
  68. }
  69. // __global__ 函数 (GPU上执行) 计算立方和
  70. // __global__ 函数 (GPU上执行) 计算立方和
  71. __global__ static void sumOfSquares(int *num, int* result, clock_t* time)
  72. {
  73. //表示目前的 thread 是第几个 thread(由 0 开始计算)0~255
  74. const int tid = threadIdx.x;
  75. //表示目前的 thread 属于第几个 block(由 0 开始计算)0~31
  76. const int bid = blockIdx.x;
  77. int sum = 0;
  78. int i;
  79. //记录运算开始的时间
  80. clock_t start;
  81. //只在 thread 0(即 threadIdx.x = 0 的时候)进行记录,每个 block 都会记录开始时间及结束时间
  82. if (tid == 0) time[bid] = clock();
  83. //thread需要同时通过tid和bid来确定,同时不要忘记保证内存连续性
  84. for (i = bid * THREAD_NUM + tid; i < DATA_SIZE; i += BLOCK_NUM * THREAD_NUM) {
  85. sum += num[i] * num[i] * num[i];
  86. }
  87. //Result的数量随之增加
  88. result[bid * THREAD_NUM + tid] = sum;
  89. //计算时间的动作,只在 thread 0(即 threadIdx.x = 0 的时候)进行,每个 block 都会记录开始时间及结束时间
  90. if (tid == 0) time[bid + BLOCK_NUM] = clock();
  91. }
  92. int main()
  93. {
  94. //CUDA 初始化
  95. if (!InitCUDA()) {
  96. return 0;
  97. }
  98. //生成随机数
  99. GenerateNumbers(data, DATA_SIZE);
  100. /*把数据复制到显卡内存中*/
  101. int* gpudata, *result;
  102. clock_t* time;
  103. //cudaMalloc 取得一块显卡内存 ( 其中result用来存储计算结果,time用来存储运行时间 )
  104. cudaMalloc((void**)&gpudata, sizeof(int)* DATA_SIZE);
  105. cudaMalloc((void**)&result, sizeof(int)*THREAD_NUM* BLOCK_NUM);
  106. cudaMalloc((void**)&time, sizeof(clock_t)* BLOCK_NUM * 2);
  107. //cudaMemcpy 将产生的随机数复制到显卡内存中
  108. //cudaMemcpyHostToDevice - 从内存复制到显卡内存
  109. //cudaMemcpyDeviceToHost - 从显卡内存复制到内存
  110. cudaMemcpy(gpudata, data, sizeof(int)* DATA_SIZE, cudaMemcpyHostToDevice);
  111. // 在CUDA 中执行函数 语法:函数名称<<<block 数目, thread 数目, shared memory 大小>>>(参数...);
  112. sumOfSquares << < BLOCK_NUM, THREAD_NUM, 0 >> >(gpudata, result, time);
  113. /*把结果从显示芯片复制回主内存*/
  114. int sum[THREAD_NUM*BLOCK_NUM];
  115. clock_t time_use[BLOCK_NUM * 2];
  116. //cudaMemcpy 将结果从显存中复制回内存
  117. cudaMemcpy(&sum, result, sizeof(int)* THREAD_NUM*BLOCK_NUM, cudaMemcpyDeviceToHost);
  118. cudaMemcpy(&time_use, time, sizeof(clock_t)* BLOCK_NUM * 2, cudaMemcpyDeviceToHost);
  119. //Free
  120. cudaFree(gpudata);
  121. cudaFree(result);
  122. cudaFree(time);
  123. int final_sum = 0;
  124. for (int i = 0; i < THREAD_NUM*BLOCK_NUM; i++) {
  125. final_sum += sum[i];
  126. }
  127. //采取新的计时策略 把每个 block 最早的开始时间,和最晚的结束时间相减,取得总运行时间
  128. clock_t min_start, max_end;
  129. min_start = time_use[0];
  130. max_end = time_use[BLOCK_NUM];
  131. for (int i = 1; i < BLOCK_NUM; i++) {
  132. if (min_start > time_use[i])
  133. min_start = time_use[i];
  134. if (max_end < time_use[i + BLOCK_NUM])
  135. max_end = time_use[i + BLOCK_NUM];
  136. }
  137. printf("GPUsum: %d time_clock: %d time: %fs\n", final_sum, max_end - min_start, ((float)(max_end - min_start) / (clockRate * 1000)));
  138. final_sum = 0;
  139. for (int i = 0; i < DATA_SIZE; i++) {
  140. final_sum += data[i] * data[i] * data[i];
  141. }
  142. printf("CPUsum: %d \n", final_sum);
  143. return 0;
  144. }

六、共享内存与同步

代码7:共享内存机制:用时:0.003568s
看似用时比上一次多了,其实主要节约了cpu中for循环的计算用时,同时也减少了数据copy的用时。总体来说,cpu和gpu的总时间减少了。
线程同步问题。即一个block中所有的线程都操作完毕了。
一个block中的所有线程可以共享内存
通常,数据的量是大于线程的总和的

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. //CUDA RunTime API
  5. #include <cuda_runtime.h>
  6. //1M
  7. #define DATA_SIZE 1048576
  8. #define THREAD_NUM 256
  9. #define BLOCK_NUM 32
  10. int clockRate = 1.0;
  11. int data[DATA_SIZE];
  12. //产生大量0-9之间的随机数
  13. void GenerateNumbers(int *number, int size)
  14. {
  15. for (int i = 0; i < size; i++) {
  16. number[i] = rand() % 10;
  17. }
  18. }
  19. //打印设备信息
  20. void printDeviceProp(const cudaDeviceProp &prop)
  21. {
  22. printf("Device Name : %s.\n", prop.name);
  23. printf("totalGlobalMem : %d.\n", prop.totalGlobalMem);
  24. printf("sharedMemPerBlock : %d.\n", prop.sharedMemPerBlock);
  25. printf("regsPerBlock : %d.\n", prop.regsPerBlock);
  26. printf("warpSize : %d.\n", prop.warpSize);
  27. printf("memPitch : %d.\n", prop.memPitch);
  28. printf("maxThreadsPerBlock : %d.\n", prop.maxThreadsPerBlock);
  29. printf("maxThreadsDim[0 - 2] : %d %d %d.\n", prop.maxThreadsDim[0], prop.maxThreadsDim[1], prop.maxThreadsDim[2]);
  30. printf("maxGridSize[0 - 2] : %d %d %d.\n", prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2]);
  31. printf("totalConstMem : %d.\n", prop.totalConstMem);
  32. printf("major.minor : %d.%d.\n", prop.major, prop.minor);
  33. printf("clockRate : %d.\n", prop.clockRate);
  34. printf("textureAlignment : %d.\n", prop.textureAlignment);
  35. printf("deviceOverlap : %d.\n", prop.deviceOverlap);
  36. printf("multiProcessorCount : %d.\n", prop.multiProcessorCount);
  37. }
  38. //CUDA 初始化
  39. bool InitCUDA()
  40. {
  41. int count;
  42. //取得支持Cuda的装置的数目
  43. cudaGetDeviceCount(&count);
  44. if (count == 0) {
  45. fprintf(stderr, "There is no device.\n");
  46. return false;
  47. }
  48. int i;
  49. for (i = 0; i < count; i++) {
  50. cudaDeviceProp prop;
  51. cudaGetDeviceProperties(&prop, i);
  52. //打印设备信息
  53. printDeviceProp(prop);
  54. //获得显卡的时钟频率
  55. clockRate = prop.clockRate;
  56. if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
  57. if (prop.major >= 1) {
  58. break;
  59. }
  60. }
  61. }
  62. if (i == count) {
  63. fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
  64. return false;
  65. }
  66. cudaSetDevice(i);
  67. return true;
  68. }
  69. // __global__ 函数 (GPU上执行) 计算立方和
  70. __global__ static void sumOfSquares(int *num, int* result, clock_t* time)
  71. {
  72. //声明一块共享内存
  73. extern __shared__ int shared[];
  74. //表示目前的 thread 是第几个 thread(由 0 开始计算)
  75. const int tid = threadIdx.x;
  76. //表示目前的 thread 属于第几个 block(由 0 开始计算)
  77. const int bid = blockIdx.x;
  78. shared[tid] = 0;
  79. int i;
  80. //记录运算开始的时间
  81. clock_t start;
  82. //只在 thread 0(即 threadIdx.x = 0 的时候)进行记录,每个 block 都会记录开始时间及结束时间
  83. if (tid == 0) time[bid] = clock();
  84. //thread需要同时通过tid和bid来确定,同时不要忘记保证内存连续性
  85. for (i = bid * THREAD_NUM + tid; i < DATA_SIZE; i += BLOCK_NUM * THREAD_NUM) {
  86. shared[tid] += num[i] * num[i] * num[i];
  87. }
  88. //同步 保证每个 thread 都已经把结果写到 shared[tid] 里面
  89. //cuda编程里,很大程度再解决如何并行和串行不出题
  90. __syncthreads();//共享内存的基础上
  91. //使用线程0完成加和
  92. if (tid == 0)
  93. {
  94. for (i = 1; i < THREAD_NUM; i++)
  95. {
  96. shared[0] += shared[i];
  97. }
  98. result[bid] = shared[0];
  99. }
  100. //计算时间的动作,只在 thread 0(即 threadIdx.x = 0 的时候)进行,每个 block 都会记录开始时间及结束时间
  101. if (tid == 0) time[bid + BLOCK_NUM] = clock();
  102. }
  103. int main()
  104. {
  105. //CUDA 初始化
  106. if (!InitCUDA()) {
  107. return 0;
  108. }
  109. //生成随机数
  110. GenerateNumbers(data, DATA_SIZE);
  111. /*把数据复制到显卡内存中*/
  112. int* gpudata, *result;
  113. clock_t* time;
  114. //cudaMalloc 取得一块显卡内存 ( 其中result用来存储计算结果,time用来存储运行时间 )
  115. cudaMalloc((void**)&gpudata, sizeof(int)* DATA_SIZE);
  116. cudaMalloc((void**)&result, sizeof(int)* BLOCK_NUM);
  117. cudaMalloc((void**)&time, sizeof(clock_t)* BLOCK_NUM * 2);
  118. //cudaMemcpy 将产生的随机数复制到显卡内存中
  119. //cudaMemcpyHostToDevice - 从内存复制到显卡内存
  120. //cudaMemcpyDeviceToHost - 从显卡内存复制到内存
  121. cudaMemcpy(gpudata, data, sizeof(int)* DATA_SIZE, cudaMemcpyHostToDevice);
  122. // 在CUDA 中执行函数 语法:函数名称<<<block 数目, thread 数目, shared memory 大小>>>(参数...);
  123. sumOfSquares << < BLOCK_NUM, THREAD_NUM, THREAD_NUM * sizeof(int) >> >(gpudata, result, time);
  124. /*把结果从显示芯片复制回主内存*/
  125. int sum[BLOCK_NUM];
  126. clock_t time_use[BLOCK_NUM * 2];
  127. //cudaMemcpy 将结果从显存中复制回内存
  128. cudaMemcpy(&sum, result, sizeof(int)*BLOCK_NUM, cudaMemcpyDeviceToHost);
  129. cudaMemcpy(&time_use, time, sizeof(clock_t)* BLOCK_NUM * 2, cudaMemcpyDeviceToHost);
  130. //Free
  131. cudaFree(gpudata);
  132. cudaFree(result);
  133. cudaFree(time);
  134. int final_sum = 0;
  135. for (int i = 0; i < BLOCK_NUM; i++) {
  136. final_sum += sum[i];
  137. }
  138. //采取新的计时策略 把每个 block 最早的开始时间,和最晚的结束时间相减,取得总运行时间
  139. clock_t min_start, max_end;
  140. min_start = time_use[0];
  141. max_end = time_use[BLOCK_NUM];
  142. for (int i = 1; i < BLOCK_NUM; i++) {
  143. if (min_start > time_use[i])
  144. min_start = time_use[i];
  145. if (max_end < time_use[i + BLOCK_NUM])
  146. max_end = time_use[i + BLOCK_NUM];
  147. }
  148. printf("GPUsum: %d time_clock: %d time: %fs\n", final_sum, max_end - min_start, ((float)(max_end - min_start) / (clockRate * 1000)));
  149. final_sum = 0;
  150. for (int i = 0; i < DATA_SIZE; i++) {
  151. final_sum += data[i] * data[i] * data[i];
  152. }
  153. printf("CPUsum: %d \n", final_sum);
  154. return 0;
  155. }

七、树状求和算法加速

代码8:树状求和算法简介: 用时:0.002941s
算法优化

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. //CUDA RunTime API
  5. #include <cuda_runtime.h>
  6. //1M
  7. #define DATA_SIZE 1048576
  8. #define THREAD_NUM 256
  9. #define BLOCK_NUM 32
  10. int clockRate = 1.0;
  11. int data[DATA_SIZE];
  12. //产生大量0-9之间的随机数
  13. void GenerateNumbers(int *number, int size)
  14. {
  15. for (int i = 0; i < size; i++) {
  16. number[i] = rand() % 10;
  17. }
  18. }
  19. //打印设备信息
  20. void printDeviceProp(const cudaDeviceProp &prop)
  21. {
  22. printf("Device Name : %s.\n", prop.name);
  23. printf("totalGlobalMem : %d.\n", prop.totalGlobalMem);
  24. printf("sharedMemPerBlock : %d.\n", prop.sharedMemPerBlock);
  25. printf("regsPerBlock : %d.\n", prop.regsPerBlock);
  26. printf("warpSize : %d.\n", prop.warpSize);
  27. printf("memPitch : %d.\n", prop.memPitch);
  28. printf("maxThreadsPerBlock : %d.\n", prop.maxThreadsPerBlock);
  29. printf("maxThreadsDim[0 - 2] : %d %d %d.\n", prop.maxThreadsDim[0], prop.maxThreadsDim[1], prop.maxThreadsDim[2]);
  30. printf("maxGridSize[0 - 2] : %d %d %d.\n", prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2]);
  31. printf("totalConstMem : %d.\n", prop.totalConstMem);
  32. printf("major.minor : %d.%d.\n", prop.major, prop.minor);
  33. printf("clockRate : %d.\n", prop.clockRate);
  34. printf("textureAlignment : %d.\n", prop.textureAlignment);
  35. printf("deviceOverlap : %d.\n", prop.deviceOverlap);
  36. printf("multiProcessorCount : %d.\n", prop.multiProcessorCount);
  37. }
  38. //CUDA 初始化
  39. bool InitCUDA()
  40. {
  41. int count;
  42. //取得支持Cuda的装置的数目
  43. cudaGetDeviceCount(&count);
  44. if (count == 0) {
  45. fprintf(stderr, "There is no device.\n");
  46. return false;
  47. }
  48. int i;
  49. for (i = 0; i < count; i++) {
  50. cudaDeviceProp prop;
  51. cudaGetDeviceProperties(&prop, i);
  52. //打印设备信息
  53. printDeviceProp(prop);
  54. //获得显卡的时钟频率
  55. clockRate = prop.clockRate;
  56. if (cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
  57. if (prop.major >= 1) {
  58. break;
  59. }
  60. }
  61. }
  62. if (i == count) {
  63. fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
  64. return false;
  65. }
  66. cudaSetDevice(i);
  67. return true;
  68. }
  69. // __global__ 函数 (GPU上执行) 计算立方和
  70. __global__ static void sumOfSquares(int *num, int* result, clock_t* time)
  71. {
  72. //声明一块共享内存
  73. extern __shared__ int shared[];
  74. //表示目前的 thread 是第几个 thread(由 0 开始计算)
  75. const int tid = threadIdx.x;
  76. //表示目前的 thread 属于第几个 block(由 0 开始计算)
  77. const int bid = blockIdx.x;
  78. shared[tid] = 0;
  79. int i;
  80. //记录运算开始的时间
  81. clock_t start;
  82. //只在 thread 0(即 threadIdx.x = 0 的时候)进行记录,每个 block 都会记录开始时间及结束时间
  83. if (tid == 0) time[bid] = clock();
  84. //thread需要同时通过tid和bid来确定,同时不要忘记保证内存连续性
  85. for (i = bid * THREAD_NUM + tid; i < DATA_SIZE; i += BLOCK_NUM * THREAD_NUM) {
  86. shared[tid] += num[i] * num[i] * num[i];
  87. }
  88. //同步 保证每个 thread 都已经把结果写到 shared[tid] 里面
  89. __syncthreads();
  90. //树状加法
  91. int offset = 1, mask = 1;
  92. while (offset < THREAD_NUM)
  93. {
  94. if ((tid & mask) == 0)
  95. {
  96. shared[tid] += shared[tid + offset];
  97. }
  98. offset += offset;
  99. mask = offset + mask;
  100. __syncthreads();
  101. }
  102. //计算时间,记录结果,只在 thread 0(即 threadIdx.x = 0 的时候)进行,每个 block 都会记录开始时间及结束时间
  103. if (tid == 0)
  104. {
  105. result[bid] = shared[0];
  106. time[bid + BLOCK_NUM] = clock();
  107. }
  108. }
  109. int main()
  110. {
  111. //CUDA 初始化
  112. if (!InitCUDA()) {
  113. return 0;
  114. }
  115. //生成随机数
  116. GenerateNumbers(data, DATA_SIZE);
  117. /*把数据复制到显卡内存中*/
  118. int* gpudata, *result;
  119. clock_t* time;
  120. //cudaMalloc 取得一块显卡内存 ( 其中result用来存储计算结果,time用来存储运行时间 )
  121. cudaMalloc((void**)&gpudata, sizeof(int)* DATA_SIZE);
  122. cudaMalloc((void**)&result, sizeof(int)* BLOCK_NUM);
  123. cudaMalloc((void**)&time, sizeof(clock_t)* BLOCK_NUM * 2);
  124. //cudaMemcpy 将产生的随机数复制到显卡内存中
  125. //cudaMemcpyHostToDevice - 从内存复制到显卡内存
  126. //cudaMemcpyDeviceToHost - 从显卡内存复制到内存
  127. cudaMemcpy(gpudata, data, sizeof(int)* DATA_SIZE, cudaMemcpyHostToDevice);
  128. // 在CUDA 中执行函数 语法:函数名称<<<block 数目, thread 数目, shared memory 大小>>>(参数...);
  129. sumOfSquares << < BLOCK_NUM, THREAD_NUM, THREAD_NUM * sizeof(int) >> >(gpudata, result, time);
  130. /*把结果从显示芯片复制回主内存*/
  131. int sum[BLOCK_NUM];
  132. clock_t time_use[BLOCK_NUM * 2];
  133. //cudaMemcpy 将结果从显存中复制回内存
  134. cudaMemcpy(&sum, result, sizeof(int)*BLOCK_NUM, cudaMemcpyDeviceToHost);
  135. cudaMemcpy(&time_use, time, sizeof(clock_t)* BLOCK_NUM * 2, cudaMemcpyDeviceToHost);
  136. //Free
  137. cudaFree(gpudata);
  138. cudaFree(result);
  139. cudaFree(time);
  140. int final_sum = 0;
  141. for (int i = 0; i < BLOCK_NUM; i++) {
  142. final_sum += sum[i];
  143. }
  144. //采取新的计时策略 把每个 block 最早的开始时间,和最晚的结束时间相减,取得总运行时间
  145. clock_t min_start, max_end;
  146. min_start = time_use[0];
  147. max_end = time_use[BLOCK_NUM];
  148. for (int i = 1; i < BLOCK_NUM; i++) {
  149. if (min_start > time_use[i])
  150. min_start = time_use[i];
  151. if (max_end < time_use[i + BLOCK_NUM])
  152. max_end = time_use[i + BLOCK_NUM];
  153. }
  154. printf("GPUsum: %d time_clock: %d time: %f\n", final_sum, max_end - min_start, ((float)(max_end - min_start) / (clockRate * 1000)));
  155. final_sum = 0;
  156. for (int i = 0; i < DATA_SIZE; i++) {
  157. final_sum += data[i] * data[i] * data[i];
  158. }
  159. printf("CPUsum: %d \n", final_sum);
  160. return 0;
  161. }

发表评论

表情:
评论列表 (有 0 条评论,101人围观)

还没有评论,来说两句吧...

相关阅读

    相关 CUDA 并行计算

    CUDA 并行计算 并行计算可以被定义为同时使用许多计算资源 (核心或计算机) 来执行并发计算,一个大的问题可以被分解成多个小问题,然后在不同的计算资源上并行处理这些小