CUDA9的编译器和语言改进

使用CUDA 9,nvcc编译器增加了对C ++ 14的支持,其中包括新功能

通用的lambda表达式,其中使用auto关键字代替参数类型;

auto lambda = [](auto a,auto b){return a * b;};

功能的返回类型扣除(使用auto关键字作为返回类型,如上例所示)

对constexpr函数可以包含的更少的限制,包括变量声明,if,switch和循环。

CUDA 9中的NVCC也更快,与CUDA 8相比,编译时间平均减少了20%,达到了50%。

·扩大开发平台和主机编译器,包括Microsoft Visual Studio 2017, Clang 3.9, PGI17.1和GCC6.x

CUDER:用C++11封装的CUDA类

以前写cuda:初始化环境,申请显存,初始化显存,launch kernel,拷贝数据,释放显存。一个页面大部分都是这些繁杂但又必须的操作,有时还会忘掉释放部分显存。

今天用C++11封装了这些CUDA操作,然后就可以专注于写kernel代码了。.cu文件就像glsl shader文件一样简洁明了。

例如:./kernel.cu文件,里面只有一个fill函数用于填充数组A。

  1. extern "C" __global__ void fill(int * A, int cnt){
  2. const int gap = blockDim.x*gridDim.x;
  3. for (int id = blockDim.x*blockIdx.x + threadIdx.x; id < cnt; id += gap)
  4. A[id] = id * 2;
  5. };

下面的main.cpp演示了Cuder类的使用。

  1. #include "Cuder.h"
  2. const int N = 65536;
  3. std::string get_ptx_path(const char*);
  4.  
  5. int main(){
  6. int A[N]; for (int i = 0; i < N; ++i) A[i] = i;
  7.  
  8. //为禁止随意创建CUcontext,将构造函数声明为private,安全起见禁用了拷贝构造函数和拷贝赋值运算符
  9. redips::Cuder cuder = redips::Cuder::getInstance();
  10.  
  11. //添加并编译一个.cu文件[相当于glsl shader 文件],或者直接添加一个ptx文件。
  12. //std::string module_file = "kernel.cu";
  13. std::string module_file = get_ptx_path("kernel.cu");
  14. cuder.addModule(module_file);
  15.  
  16. //显存上申请一个大小为[sizeof(int)*N]的数组,并将其命名为["a_dev"],用于后面操作中该数组的标识;
  17. //如果第三个参数不为null,还会执行cpu->gpu的数据拷贝
  18. cuder.applyArray("a_dev", sizeof(int)*N, A);
  19.  
  20. //运行["./kernel.cu"]文件中指定的["fill"]函数, 前两个参数设定了gridSize和blockSize
  21. //{ "a_dev", N }是C++11中的initializer_list, 如果是字符串则对应前面申请的显存数组名,否则是变量类型
  22. cuder.launch(dim3(512, 1, 1), dim3(256, 1, 1), module_file, "fill", { "a_dev", N });
  23.  
  24. //将["a_dev"]对应的显存数组拷贝回[A]
  25. cuder.fetchArray("a_dev", sizeof(int)*N, A);
  26. return 0;
  27. }
  28.  
  29. std::string get_ptx_path(const char* cuFile){
  30. std::string path = "./ptx/";
  31.  
  32. #ifdef WIN32
  33. path += "Win32/";
  34. #else
  35. path += "x64/";
  36. #endif
  37.  
  38. #ifdef _DEBUG
  39. path += "Debug/";
  40. #else
  41. path += "Release/";
  42. #endif
  43. return path + cuFile + ".ptx";
  44. }

cuder.addModule(...)函数的参数是一个.cu文件或者.ptx文件。

  1. 1. 如果是.cu文件,该函数负责将函数编译成ptx代码。然后封装到CUmodule里。
  2. 2. 如果是.ptx文件,该函数只是将ptx封装到CUmodule里。
  3. 建议使用第二种方式,nvidiaoptix就是这么做的。好处是在编译阶段编译总比运行时编译好,如果代码有错误编译时就会提示。这时需要两点配置:
  4. 2.a 在生成依赖项里添加cuda 编译器,然后相应的.cu文件设定为用该编译器编译。
  5. 2.b 设定将.cu文件生成到指定路径下的ptx文件,然后在程序中指定该ptx文件的路径。

下面贴上Cuder.h的代码


  1. #pragma once
  2. #include <map>
  3. #include <string>
  4. #include <vector>
  5. #include <cuda.h>
  6. #include <nvrtc.h>
  7. #include <fstream>
  8. #include <sstream>
  9. #include <iostream>
  10. #include <cudaProfiler.h>
  11. #include <cuda_runtime.h>
  12. #include <helper_cuda_drvapi.h>
  13.  
  14. namespace redips{
  15. class Cuder{
  16. CUcontext context;
  17. std::map <std::string, CUmodule> modules;
  18. std::map <std::string, CUdeviceptr> devptrs;
  19.  
  20. Cuder(){
  21. checkCudaErrors(cuCtxCreate(&context, 0, cuDevice));
  22. }
  23. void release(){
  24. //for (auto module : modules) delete module.second;
  25. for (auto dptr : devptrs) cuMemFree(dptr.second);
  26. devptrs.clear();
  27. modules.clear();
  28. cuCtxDestroy(context);
  29. }
  30. public:
  31. class ValueHolder{
  32. public:
  33. void * value = nullptr;
  34. bool is_string = false;
  35. ValueHolder(const char* str){
  36. value = (void*)str;
  37. is_string = true;
  38. }
  39. template <typename T>
  40. ValueHolder(const T& data){
  41. value = new T(data);
  42. }
  43. };
  44.  
  45. static Cuder getInstance(){
  46. if (!cuda_enviroment_initialized) initialize();
  47. return Cuder();
  48. }
  49.  
  50. //forbidden copy-constructor and assignment function
  51. Cuder(const Cuder&) = delete;
  52. Cuder& operator= (const Cuder& another) = delete;
  53.  
  54. Cuder(Cuder&& another){
  55. this->context = another.context;
  56. another.context = nullptr;
  57. this->devptrs = std::map<std::string, CUdeviceptr>(std::move(another.devptrs));
  58. this->modules = std::map<std::string, CUmodule>(std::move(another.modules));
  59. }
  60. Cuder& operator= (Cuder&& another) {
  61. if (this->context == another.context) return *this;
  62. release();
  63. this->context = another.context;
  64. another.context = nullptr;
  65. this->devptrs = std::map<std::string, CUdeviceptr>(std::move(another.devptrs));
  66. this->modules = std::map<std::string, CUmodule>(std::move(another.modules));
  67. return *this;
  68. }
  69.  
  70. virtual ~Cuder(){ release(); };
  71.  
  72. public:
  73. bool launch(dim3 gridDim, dim3 blockDim, std::string module, std::string kernel_function, std::initializer_list<ValueHolder> params){
  74. //get kernel address
  75. if (!modules.count(module)){
  76. std::cerr << "[Cuder] : error: doesn't exists an module named " << module << std::endl; return false;
  77. }
  78. CUfunction kernel_addr;
  79. if (CUDA_SUCCESS != cuModuleGetFunction(&kernel_addr, modules[module], kernel_function.c_str())){
  80. std::cerr << "[Cuder] : error: doesn't exists an kernel named " << kernel_function << " in module " << module << std::endl; return false;
  81. }
  82. //setup params
  83. std::vector<void*> pamary;
  84. for (auto v : params){
  85. if (v.is_string){
  86. if (devptrs.count((const char*)(v.value))) pamary.push_back((void*)(&(devptrs[(const char*)(v.value)])));
  87. else{
  88. std::cerr << "[Cuder] : error: launch failed. doesn't exists an array named " << (const char*)(v.value) << std::endl;;
  89. return false;
  90. }
  91. }
  92. else pamary.push_back(v.value);
  93. }
  94.  
  95. cudaEvent_t start, stop;
  96. float elapsedTime = 0.0;
  97. cudaEventCreate(&start);
  98. cudaEventCreate(&stop);
  99. cudaEventRecord(start, 0);
  100.  
  101. bool result = (CUDA_SUCCESS == cuLaunchKernel(kernel_addr,/* grid dim */gridDim.x, gridDim.y, gridDim.z, /* block dim */blockDim.x, blockDim.y, blockDim.z, /* shared mem, stream */ 0, 0, &pamary[0], /* arguments */0));
  102. cuCtxSynchronize();
  103.  
  104. cudaEventRecord(stop, 0);
  105. cudaEventSynchronize(stop);
  106. cudaEventElapsedTime(&elapsedTime, start, stop);
  107. std::cout << "[Cuder] : launch finish. cost " << elapsedTime << "ms" << std::endl;
  108. return result;
  109. }
  110. bool addModule(std::string cufile){
  111. if (modules.count(cufile)){
  112. std::cerr << "[Cuder] : error: already has an modules named " << cufile << std::endl;;
  113. return false;
  114. }
  115.  
  116. std::string ptx = get_ptx(cufile);
  117.  
  118. if (ptx.length() > 0){
  119. CUmodule module;
  120. checkCudaErrors(cuModuleLoadDataEx(&module, ptx.c_str(), 0, 0, 0));
  121. modules[cufile] = module;
  122. return true;
  123. }
  124. else{
  125. std::cerr << "[Cuder] : error: add module " << cufile << " failed!\n";
  126. return false;
  127. }
  128. }
  129. void applyArray(const char* name, size_t size, void* h_ptr=nullptr){
  130. if (devptrs.count(name)){
  131. std::cerr << "[Cuder] : error: already has an array named " << name << std::endl;;
  132. return;
  133. }
  134. CUdeviceptr d_ptr;
  135. checkCudaErrors(cuMemAlloc(&d_ptr, size));
  136. if (h_ptr)
  137. checkCudaErrors(cuMemcpyHtoD(d_ptr, h_ptr, size));
  138. devptrs[name] = d_ptr;
  139. }
  140. void fetchArray(const char* name, size_t size,void * h_ptr){
  141. if (!devptrs.count(name)){
  142. std::cerr << "[Cuder] : error: doesn't exists an array named " << name << std::endl;;
  143. return;
  144. }
  145. checkCudaErrors(cuMemcpyDtoH(h_ptr, devptrs[name], size));
  146. }
  147.  
  148. private:
  149. static int devID;
  150. static CUdevice cuDevice;
  151. static bool cuda_enviroment_initialized;
  152. static void initialize(){
  153. // picks the best CUDA device [with highest Gflops/s] available
  154. devID = gpuGetMaxGflopsDeviceIdDRV();
  155. checkCudaErrors(cuDeviceGet(&cuDevice, devID));
  156. // print device information
  157. {
  158. char name[100]; int major = 0, minor = 0;
  159. checkCudaErrors(cuDeviceGetName(name, 100, cuDevice));
  160. checkCudaErrors(cuDeviceComputeCapability(&major, &minor, cuDevice));
  161. printf("[Cuder] : Using CUDA Device [%d]: %s, %d.%d compute capability\n", devID, name, major, minor);
  162. }
  163. //initialize
  164. checkCudaErrors(cuInit(0));
  165.  
  166. cuda_enviroment_initialized = true;
  167. }
  168. //如果是ptx文件则直接返回文件内容,如果是cu文件则编译后返回ptx
  169. std::string get_ptx(std::string filename){
  170. std::ifstream inputFile(filename, std::ios::in | std::ios::binary | std::ios::ate);
  171. if (!inputFile.is_open()) {
  172. std::cerr << "[Cuder] : error: unable to open " << filename << " for reading!\n";
  173. return "";
  174. }
  175.  
  176. std::streampos pos = inputFile.tellg();
  177. size_t inputSize = (size_t)pos;
  178. char * memBlock = new char[inputSize + 1];
  179.  
  180. inputFile.seekg(0, std::ios::beg);
  181. inputFile.read(memBlock, inputSize);
  182. inputFile.close();
  183. memBlock[inputSize] = '\x0';
  184.  
  185. if (filename.find(".ptx") != std::string::npos)
  186. return std::string(std::move(memBlock));
  187. // compile
  188. nvrtcProgram prog;
  189. if (nvrtcCreateProgram(&prog, memBlock, filename.c_str(), 0, NULL, NULL) == NVRTC_SUCCESS){
  190. delete memBlock;
  191. if (nvrtcCompileProgram(prog, 0, nullptr) == NVRTC_SUCCESS){
  192. // dump log
  193. size_t logSize;
  194. nvrtcGetProgramLogSize(prog, &logSize);
  195. if (logSize>0){
  196. char *log = new char[logSize + 1];
  197. nvrtcGetProgramLog(prog, log);
  198. log[logSize] = '\x0';
  199. std::cout << "[Cuder] : compile [" << filename << "] " << log << std::endl;
  200. delete(log);
  201. }
  202. else std::cout << "[Cuder] : compile [" << filename << "] finish" << std::endl;
  203.  
  204. // fetch PTX
  205. size_t ptxSize;
  206. nvrtcGetPTXSize(prog, &ptxSize);
  207. char *ptx = new char[ptxSize+1];
  208. nvrtcGetPTX(prog, ptx);
  209. nvrtcDestroyProgram(&prog);
  210. return std::string(std::move(ptx));
  211. }
  212. }
  213. delete memBlock;
  214. return "";
  215. }
  216. };
  217. bool Cuder::cuda_enviroment_initialized = false;
  218. int Cuder::devID = 0;
  219. CUdevice Cuder::cuDevice = 0;
  220. };

下面贴一下VS里面需要的配置


  1. //include
  2. C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\include
  3. C:\ProgramData\NVIDIA Corporation\CUDA Samples\v7.5\common\inc
  4. //lib
  5. C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.5\lib\x64
  6.  
  7. cuda.lib
  8. cudart.lib
  9. nvrtc.lib

CUDA 显存操作:CUDA支持的C++11的更多相关文章

  1. 显卡、显卡驱动、显存、GPU、CUDA、cuDNN

    显卡 Video card,Graphics card,又叫显示接口卡,是一个硬件概念(相似的还有网卡),执行计算机到显示设备的数模信号转换任务,安装在计算机的主板上,将计算机的数字信号转换成模拟 ...

  2. 6G显卡显存不足出现CUDA Error:out of memory解决办法

    ​ 从6月初开始,6G显存的显卡开始出现CUDA Error:out of memory的问题,这是因为dag文件一直在增加,不过要增加到6G还需要最少两年的时间. 现在出现问题的原因是1.内核太古老 ...

  3. 自制操作系统Antz(3)——进入保护模式 (中) 直接操作显存

    Antz系统更新地址: https://www.cnblogs.com/LexMoon/category/1262287.html Linux内核源码分析地址:https://www.cnblogs. ...

  4. windows 10 上源码编译OpenCV并支持CUDA | compile opencv with CUDA support on windows 10

    本文首发于个人博客https://kezunlin.me/post/6580691f/,欢迎阅读! compile opencv with CUDA support on windows 10 Ser ...

  5. AI换脸必备知识:如何查看显卡型号以及显存大小!

    使用Deepfakes(AI换脸) 软件,拼的就是配置,耗的就是时间,考验的是耐心. 配置好了,时间就少了. 所以玩这种软件,硬核需求就是:配置,配置,配置.  我的电脑能跑这个软件么?也是很多新手的 ...

  6. 我的Keras使用总结(5)——Keras指定显卡且限制显存用量,常见函数的用法及其习题练习

    Keras 是一个高层神经网络API,Keras是由纯Python编写而成并基于TensorFlow,Theano以及CNTK后端.Keras为支持快速实验而生,能够将我们的idea迅速转换为结果.好 ...

  7. [Pytorch]深度模型的显存计算以及优化

    原文链接:https://oldpan.me/archives/how-to-calculate-gpu-memory 前言 亲,显存炸了,你的显卡快冒烟了! torch.FatalError: cu ...

  8. 深度学习中GPU和显存分析

    刚入门深度学习时,没有显存的概念,后来在实验中才渐渐建立了这个意识. 下面这篇文章很好的对GPU和显存总结了一番,于是我转载了过来. 作者:陈云 链接:https://zhuanlan.zhihu. ...

  9. Tensorflow与Keras自适应使用显存

    Tensorflow支持基于cuda内核与cudnn的GPU加速,Keras出现较晚,为Tensorflow的高层框架,由于Keras使用的方便性与很好的延展性,之后更是作为Tensorflow的官方 ...

随机推荐

  1. Ubuntu 16.04 LTS 搭建LAMP

    1. LAMP是一系列自由和开源软件的集合,包含了Linux.Web服务器(Apache).数据库服务器(MySQL)和PHP(脚本语言). Apache2 Web 服务器的安装 sudo apt i ...

  2. git remote加入本地库的方法

    方法来自airk: 假设须要将你电脑本地的一个git库(目录)B 加入到另外一个git库(目录) A的 remote里 操作方法例如以下: 先在git仓库B操作: git init --bare 然后 ...

  3. C#.NET 如何打开高版本的sln文件

    我用VS2008去打开2010版本的解决方案,提示如下   其实我可以直接打开这个csproj文件并运行   关闭之后就会提示是否创建一个新的 sln文件

  4. nodejs v8引擎c++编译版本号升级教程

    原GCC版本号:4.4.7. 目标:升级GCC到4.8.2.以支持C++11. yum install gcc-c++ 获取GCC 4.8.2包:wget http://gcc.skazkaforyo ...

  5. 使用requireJS的shim參数,完毕jquery插件的载入

    没有requireJS框架之前,假设我们想使用jquery框架,会在HTML页面中通过<script>标签载入.这个时候jquery框架生成全局变量$和jQuery等全局变量.假设项目中引 ...

  6. checkstyle+ant生成checkstyle报告

    <?xml version="1.0" encoding="UTF-8" ?> <project name="tibim" ...

  7. react-color 颜色选择器组件

    demo链接:github demo 安装: npm install react-color --save 有一下几种类型组件 <AlphaPicker /> <BlockPicke ...

  8. kafka 生产者消费者 api接口

    生产者 import java.util.Properties; import kafka.javaapi.producer.Producer; import kafka.producer.Keyed ...

  9. 混合使用Delphi和C ++(附下载)

    您想将C ++添加到Delphi应用程序中吗?或者将Delphi代码添加到C ++应用程序中?这是如何做. 您可能不知道的一件事是如何在RAD Studio中集成C ++和Delphi语言.您可以将单 ...

  10. Oracle利用游标返回结果集的的例子(C#)...(最爱)

    引用地址:http://www.alixixi.com/program/a/2008050727634.shtml   本例在VS2005+Oracle 92010 + WindowsXp Sp2测试 ...