深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题。这里主要记录自己的GPU自学历程。

目录

三、 CUDA程序初探

3.1 主机与设备

通常将CPU及其内存称之为主机,GPU及其内存称之为设备。

如下图所示,新建一个NVIDIA CUDA工程,并命名为 “1-helloworld”

之后发现项目里多了一个 “kernel.cu”的文件,该文件内容是一个经典的 矢量相加 的GPU程序。

可以暂时全部注释该代码,并尝试编译运行下面的我们经常见到的编程入门示例:

  1. #include <iostream>
  2. int main()
  3. {
  4. std::cout<<"Hello, World!"<<std::endl;
  5. system("pause");
  6. return 0;
  7. }

这看起来和普通的C++程序并没什么区别。 这个示例只是为了说明CUDA C编程和我们熟悉的标准C在很大程度上是没有区别的。 同时,这段程序直接运行在 主机上。

接下来,我们看看如何使用GPU来执行代码。如下:

  1. #include <iostream>
  2. __global__ void mkernel(void){}
  3. int main()
  4. {
  5. mkernel <<<1,1>>>();
  6. std::cout<<"Hello, World!"<<std::endl;
  7. system("pause");
  8. return 0;
  9. }

与之前的代码相比, 这里主要增加了

  • 一个空的函数mkernel(), 并带有修饰符 global
  • 对空函数的调用, 并带有修饰符 <<<1,1>>>

_global_ 为CUDA C为标准C增加的修饰符,表示该函数将会交给编译设备代码的编译器(NVCC)并最终在设备上运行。 而 main函数则依旧交给系统编译器(VS2013)。

其实,CUDA就是通过直接提供API接口或者在语言层面集成一些新的东西来实现在主机代码中调用设备代码。

3.2 第一个GPU程序: 矢量相加

下面主要通过代码解读的形式来进行我们的第一个GPU程序。

程序遵循以下流程:

主机端准备数据 -> 数据复制到GPU内存中 -> GPU执行核函数 -> 数据由GPU取回到主机

  1. #include "cuda_runtime.h"
  2. #include "device_launch_parameters.h"
  3. #include <stdio.h>
  4. // 接口函数: 主机代码调用GPU设备实现矢量加法 c = a + b
  5. cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);
  6. // 核函数:每个线程负责一个分量的加法
  7. __global__ void addKernel(int *c, const int *a, const int *b)
  8. {
  9. int i = threadIdx.x; // 获取线程ID
  10. c[i] = a[i] + b[i];
  11. }
  12. int main()
  13. {
  14. const int arraySize = 5;
  15. const int a[arraySize] = { 1, 2, 3, 4, 5 };
  16. const int b[arraySize] = { 10, 20, 30, 40, 50 };
  17. int c[arraySize] = { 0 };
  18. // 并行矢量相加
  19. cudaError_t cudaStatus = addWithCuda(c, a, b, arraySize);
  20. if (cudaStatus != cudaSuccess) {
  21. fprintf(stderr, "addWithCuda failed!");
  22. return 1;
  23. }
  24. printf("{1,2,3,4,5} + {10,20,30,40,50} = {%d,%d,%d,%d,%d}\n",
  25. c[0], c[1], c[2], c[3], c[4]);
  26. // CUDA设备重置,以便其它性能检测和跟踪工具的运行,如Nsight and Visual Profiler to show complete traces.traces.
  27. cudaStatus = cudaDeviceReset();
  28. if (cudaStatus != cudaSuccess) {
  29. fprintf(stderr, "cudaDeviceReset failed!");
  30. return 1;
  31. }
  32. return 0;
  33. }
  34. // 接口函数实现: 主机代码调用GPU设备实现矢量加法 c = a + b
  35. cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)
  36. {
  37. int *dev_a = 0;
  38. int *dev_b = 0;
  39. int *dev_c = 0;
  40. cudaError_t cudaStatus;
  41. // 选择程序运行在哪块GPU上,(多GPU机器可以选择)
  42. cudaStatus = cudaSetDevice(0);
  43. if (cudaStatus != cudaSuccess) {
  44. fprintf(stderr, "cudaSetDevice failed! Do you have a CUDA-capable GPU installed?");
  45. goto Error;
  46. }
  47. // 依次为 c = a + b三个矢量在GPU上开辟内存 .
  48. cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));
  49. if (cudaStatus != cudaSuccess) {
  50. fprintf(stderr, "cudaMalloc failed!");
  51. goto Error;
  52. }
  53. cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));
  54. if (cudaStatus != cudaSuccess) {
  55. fprintf(stderr, "cudaMalloc failed!");
  56. goto Error;
  57. }
  58. cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));
  59. if (cudaStatus != cudaSuccess) {
  60. fprintf(stderr, "cudaMalloc failed!");
  61. goto Error;
  62. }
  63. // 将矢量a和b依次copy进入GPU内存中
  64. cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
  65. if (cudaStatus != cudaSuccess) {
  66. fprintf(stderr, "cudaMemcpy failed!");
  67. goto Error;
  68. }
  69. cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);
  70. if (cudaStatus != cudaSuccess) {
  71. fprintf(stderr, "cudaMemcpy failed!");
  72. goto Error;
  73. }
  74. // 运行核函数,运行设置为1个block,每个block中size个线程
  75. addKernel<<<1, size>>>(dev_c, dev_a, dev_b);
  76. // 检查是否出现了错误
  77. cudaStatus = cudaGetLastError();
  78. if (cudaStatus != cudaSuccess) {
  79. fprintf(stderr, "addKernel launch failed: %s\n", cudaGetErrorString(cudaStatus));
  80. goto Error;
  81. }
  82. // 停止CPU端线程的执行,直到GPU完成之前CUDA的任务,包括kernel函数、数据拷贝等
  83. cudaStatus = cudaDeviceSynchronize();
  84. if (cudaStatus != cudaSuccess) {
  85. fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
  86. goto Error;
  87. }
  88. // 将计算结果从GPU复制到主机内存
  89. cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);
  90. if (cudaStatus != cudaSuccess) {
  91. fprintf(stderr, "cudaMemcpy failed!");
  92. goto Error;
  93. }
  94. Error:
  95. cudaFree(dev_c);
  96. cudaFree(dev_a);
  97. cudaFree(dev_b);
  98. return cudaStatus;
  99. }

参考资料:

  1. 《CUDA by Example: An Introduction to General-Purpose GPU Programming》 中文名《GPU高性能编程CUDA实战》

GPU编程自学3 —— CUDA程序初探的更多相关文章

  1. GPU编程自学4 —— CUDA核函数运行参数

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  2. GPU编程自学2 —— CUDA环境配置

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  3. GPU编程自学7 —— 常量内存与事件

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  4. GPU编程自学6 —— 函数与变量类型限定符

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  5. GPU编程自学5 —— 线程协作

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  6. GPU编程自学1 —— 引言

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  7. 第一篇:GPU 编程技术的发展历程及现状

    前言 本文通过介绍 GPU 编程技术的发展历程,让大家初步地了解 GPU 编程,走进 GPU 编程的世界. 冯诺依曼计算机架构的瓶颈 曾经,几乎所有的处理器都是以冯诺依曼计算机架构为基础的.该系统架构 ...

  8. GPU 编程入门到精通(五)之 GPU 程序优化进阶

    博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识.鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程. 有志同道合的小伙 ...

  9. GPU/CUDA程序初体验 向量加法

    现在主要的并行计算设备有两种发展趋势: (1)多核CPU. 双核,四核,八核,...,72核,...,可以使用OpenMP编译处理方案,就是指导编译器编译为多核并行执行. (2)多线程设备(GP)GP ...

随机推荐

  1. 20145301 《Java程序设计》第八周学习总结

    20145301 <Java程序设计>第八周学习总结 教材学习内容总结 第十五章部分 - 通用API 日志API 日志: 日志对信息安全意义重大,审计.取证.入侵检测等都会用到日志信息 L ...

  2. Java学习第三周摘要

    20145307<Java程序设计>第三周学习总结 教材学习内容总结 认识对象 类类型 Java可区分为基本类型和类类型两大类型系统,其中类类型也称为参考类型.sun就是一个类类型变量,类 ...

  3. 20145321 《Java程序设计》第2周学习总结

    20145321 <Java程序设计>第2周学习总结 教材学习内容总结 一.类型.变量.运算符 1.类型(基本类型) (1)整数:short(占2字节),int(占4字节),long(占8 ...

  4. CSS样式遇见的问题总结记录

    一.子元素都是浮动元素时,父元素最好是不用设置高度,防止子元素不设置高度溢出父元素 有时候会有零点几的误差高度 直接设置子元素高度即可 通过 clear: both;清除子元素浮动达到父元素自适应高度 ...

  5. ETL增量处理总结

    1 LOG表 1.1 思路 用log表记录业务库某表yw_tableA发生变化数据的主键.数据进入BI库目标表bi_tableA前,先根据log表记录的主键进行delete. 1.2 设计 1.2.1 ...

  6. 02_zookeeper集群安装

    zookeeper集群安装 (1)   下载zookeeper安装包,并上传到要组成zookeeper集群的多个机器上 我放置的目录:/usr/local/src/zookeeper-3.4.5.ta ...

  7. MySQL表锁和行锁

    锁粒度 MySQL 不同的存储引擎支持不同的锁机制,所有的存储引擎都以自己的方式显现了锁机制,服务器层完全不了解存储引擎中的锁实现: InnoDB 存储引擎既支持行级锁(row-level locki ...

  8. [mybatis]Mapper XML 文件——statementType

    statementType:STATEMENT,PREPARED 或 CALLABLE(存储过程) 的一个.这会让 MyBatis 分别使用 Statement,PreparedStatement 或 ...

  9. css 权威指南笔记

    部分属性选择: 选择class 属性中包含warning的元素 [class~="warning"]{font-weight:bold} 子串匹配属性选择器: 在现代浏览器中得到支 ...

  10. 关闭多个screen

    由于开了很多个screen同时工作,关闭是一个一个比较麻烦,写个命令在这以便日后想不起来时可以用到. 1.先看看有多少个screen    screen -ls |awk '/Socket/'|awk ...