Cuder - 用C++11封装的CUDA类
以前写cuda:初始化环境,申请显存,初始化显存,launch kernel,拷贝数据,释放显存。一个页面大部分都是这些繁杂但又必须的操作,有时还会忘掉释放部分显存。
今天用C++11封装了这些CUDA操作,然后就可以专注于写kernel代码了。.cu文件就像glsl shader文件一样简洁明了。
例如:./kernel.cu文件,里面只有一个fill函数用于填充数组A。
extern "C" __global__ void fill(int * A, int cnt){
const int gap = blockDim.x*gridDim.x;
for (int id = blockDim.x*blockIdx.x + threadIdx.x; id < cnt; id += gap)
A[id] = id * ;
};
下面的main.cpp演示了Cuder类的使用。
#include "Cuder.h"
const int N = ;
std::string get_ptx_path(const char*); int main(){
int A[N]; for (int i = ; i < N; ++i) A[i] = i; //为禁止随意创建CUcontext,将构造函数声明为private,安全起见禁用了拷贝构造函数和拷贝赋值运算符
redips::Cuder cuder = redips::Cuder::getInstance(); //添加并编译一个.cu文件[相当于glsl shader 文件],或者直接添加一个ptx文件。
//std::string module_file = "kernel.cu";
std::string module_file = get_ptx_path("kernel.cu");
cuder.addModule(module_file); //显存上申请一个大小为[sizeof(int)*N]的数组,并将其命名为["a_dev"],用于后面操作中该数组的标识;
//如果第三个参数不为null,还会执行cpu->gpu的数据拷贝
cuder.applyArray("a_dev", sizeof(int)*N, A); //运行["./kernel.cu"]文件中指定的["fill"]函数, 前两个参数设定了gridSize和blockSize
//{ "a_dev", N }是C++11中的initializer_list, 如果是字符串则对应前面申请的显存数组名,否则是变量类型
cuder.launch(dim3(, , ), dim3(, , ), module_file, "fill", { "a_dev", N }); //将["a_dev"]对应的显存数组拷贝回[A]
cuder.fetchArray("a_dev", sizeof(int)*N, A);
return ;
} std::string get_ptx_path(const char* cuFile){
std::string path = "./ptx/"; #ifdef WIN32
path += "Win32/";
#else
path += "x64/";
#endif #ifdef _DEBUG
path += "Debug/";
#else
path += "Release/";
#endif
return path + cuFile + ".ptx";
}
cuder.addModule(...)函数的参数是一个.cu文件或者.ptx文件。
1. 如果是.cu文件,该函数负责将函数编译成ptx代码。然后封装到CUmodule里。
2. 如果是.ptx文件,该函数只是将ptx封装到CUmodule里。
建议使用第二种方式,nvidia的optix就是这么做的。好处是在编译阶段编译总比运行时编译好,如果代码有错误编译时就会提示。这时需要两点配置:
2.a 在生成依赖项里添加cuda 编译器,然后相应的.cu文件设定为用该编译器编译。
2.b 设定将.cu文件生成到指定路径下的ptx文件,然后在程序中指定该ptx文件的路径。

下面贴上Cuder.h的代码
#pragma once
#include <map>
#include <string>
#include <vector>
#include <cuda.h>
#include <nvrtc.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <cudaProfiler.h>
#include <cuda_runtime.h>
#include <helper_cuda_drvapi.h> namespace redips{
class Cuder{
CUcontext context;
std::map <std::string, CUmodule> modules;
std::map <std::string, CUdeviceptr> devptrs; Cuder(){
checkCudaErrors(cuCtxCreate(&context, , cuDevice));
}
void release(){
//for (auto module : modules) delete module.second;
for (auto dptr : devptrs) cuMemFree(dptr.second);
devptrs.clear();
modules.clear();
cuCtxDestroy(context);
}
public:
class ValueHolder{
public:
void * value = nullptr;
bool is_string = false;
ValueHolder(const char* str){
value = (void*)str;
is_string = true;
}
template <typename T>
ValueHolder(const T& data){
value = new T(data);
}
}; static Cuder getInstance(){
if (!cuda_enviroment_initialized) initialize();
return Cuder();
} //forbidden copy-constructor and assignment function
Cuder(const Cuder&) = delete;
Cuder& operator= (const Cuder& another) = delete; Cuder(Cuder&& another){
this->context = another.context;
another.context = nullptr;
this->devptrs = std::map<std::string, CUdeviceptr>(std::move(another.devptrs));
this->modules = std::map<std::string, CUmodule>(std::move(another.modules));
}
Cuder& operator= (Cuder&& another) {
if (this->context == another.context) return *this;
release();
this->context = another.context;
another.context = nullptr;
this->devptrs = std::map<std::string, CUdeviceptr>(std::move(another.devptrs));
this->modules = std::map<std::string, CUmodule>(std::move(another.modules));
return *this;
} virtual ~Cuder(){ release(); }; public:
bool launch(dim3 gridDim, dim3 blockDim, std::string module, std::string kernel_function, std::initializer_list<ValueHolder> params){
//get kernel address
if (!modules.count(module)){
std::cerr << "[Cuder] : error: doesn't exists an module named " << module << std::endl; return false;
}
CUfunction kernel_addr;
if (CUDA_SUCCESS != cuModuleGetFunction(&kernel_addr, modules[module], kernel_function.c_str())){
std::cerr << "[Cuder] : error: doesn't exists an kernel named " << kernel_function << " in module " << module << std::endl; return false;
}
//setup params
std::vector<void*> pamary;
for (auto v : params){
if (v.is_string){
if (devptrs.count((const char*)(v.value))) pamary.push_back((void*)(&(devptrs[(const char*)(v.value)])));
else{
std::cerr << "[Cuder] : error: launch failed. doesn't exists an array named " << (const char*)(v.value) << std::endl;;
return false;
}
}
else pamary.push_back(v.value);
} cudaEvent_t start, stop;
float elapsedTime = 0.0;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, ); 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 */ , , &pamary[], /* arguments */));
cuCtxSynchronize(); cudaEventRecord(stop, );
cudaEventSynchronize(stop);
cudaEventElapsedTime(&elapsedTime, start, stop);
std::cout << "[Cuder] : launch finish. cost " << elapsedTime << "ms" << std::endl;
return result;
}
bool addModule(std::string cufile){
if (modules.count(cufile)){
std::cerr << "[Cuder] : error: already has an modules named " << cufile << std::endl;;
return false;
} std::string ptx = get_ptx(cufile); if (ptx.length() > ){
CUmodule module;
checkCudaErrors(cuModuleLoadDataEx(&module, ptx.c_str(), , , ));
modules[cufile] = module;
return true;
}
else{
std::cerr << "[Cuder] : error: add module " << cufile << " failed!\n";
return false;
}
}
void applyArray(const char* name, size_t size, void* h_ptr=nullptr){
if (devptrs.count(name)){
std::cerr << "[Cuder] : error: already has an array named " << name << std::endl;;
return;
}
CUdeviceptr d_ptr;
checkCudaErrors(cuMemAlloc(&d_ptr, size));
if (h_ptr)
checkCudaErrors(cuMemcpyHtoD(d_ptr, h_ptr, size));
devptrs[name] = d_ptr;
}
void fetchArray(const char* name, size_t size,void * h_ptr){
if (!devptrs.count(name)){
std::cerr << "[Cuder] : error: doesn't exists an array named " << name << std::endl;;
return;
}
checkCudaErrors(cuMemcpyDtoH(h_ptr, devptrs[name], size));
} private:
static int devID;
static CUdevice cuDevice;
static bool cuda_enviroment_initialized;
static void initialize(){
// picks the best CUDA device [with highest Gflops/s] available
devID = gpuGetMaxGflopsDeviceIdDRV();
checkCudaErrors(cuDeviceGet(&cuDevice, devID));
// print device information
{
char name[]; int major = , minor = ;
checkCudaErrors(cuDeviceGetName(name, , cuDevice));
checkCudaErrors(cuDeviceComputeCapability(&major, &minor, cuDevice));
printf("[Cuder] : Using CUDA Device [%d]: %s, %d.%d compute capability\n", devID, name, major, minor);
}
//initialize
checkCudaErrors(cuInit()); cuda_enviroment_initialized = true;
}
//如果是ptx文件则直接返回文件内容,如果是cu文件则编译后返回ptx
std::string get_ptx(std::string filename){
std::ifstream inputFile(filename, std::ios::in | std::ios::binary | std::ios::ate);
if (!inputFile.is_open()) {
std::cerr << "[Cuder] : error: unable to open " << filename << " for reading!\n";
return "";
} std::streampos pos = inputFile.tellg();
size_t inputSize = (size_t)pos;
char * memBlock = new char[inputSize + ]; inputFile.seekg(, std::ios::beg);
inputFile.read(memBlock, inputSize);
inputFile.close();
memBlock[inputSize] = '\x0'; if (filename.find(".ptx") != std::string::npos)
return std::string(std::move(memBlock));
// compile
nvrtcProgram prog;
if (nvrtcCreateProgram(&prog, memBlock, filename.c_str(), , NULL, NULL) == NVRTC_SUCCESS){
delete memBlock;
if (nvrtcCompileProgram(prog, , nullptr) == NVRTC_SUCCESS){
// dump log
size_t logSize;
nvrtcGetProgramLogSize(prog, &logSize);
if (logSize>){
char *log = new char[logSize + ];
nvrtcGetProgramLog(prog, log);
log[logSize] = '\x0';
std::cout << "[Cuder] : compile [" << filename << "] " << log << std::endl;
delete(log);
}
else std::cout << "[Cuder] : compile [" << filename << "] finish" << std::endl; // fetch PTX
size_t ptxSize;
nvrtcGetPTXSize(prog, &ptxSize);
char *ptx = new char[ptxSize+];
nvrtcGetPTX(prog, ptx);
nvrtcDestroyProgram(&prog);
return std::string(std::move(ptx));
}
}
delete memBlock;
return "";
}
};
bool Cuder::cuda_enviroment_initialized = false;
int Cuder::devID = ;
CUdevice Cuder::cuDevice = ;
};
下面贴一下VS里面需要的配置
//include
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.\include
C:\ProgramData\NVIDIA Corporation\CUDA Samples\v7.\common\inc
//lib
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.\lib\x64 cuda.lib
cudart.lib
nvrtc.lib
Cuder - 用C++11封装的CUDA类的更多相关文章
- python+pytest接口自动化(11)-测试函数、测试类/测试方法的封装
前言 在python+pytest 接口自动化系列中,我们之前的文章基本都没有将代码进行封装,但实际编写自动化测试脚本中,我们都需要将测试代码进行封装,才能被测试框架识别执行. 例如单个接口的请求代码 ...
- jdbc 11: 封装自己的jdbc工具类
jdbc连接mysql,封装自己的jdbc工具类 package com.examples.jdbc.utils; import java.sql.*; import java.util.Resour ...
- MySQL JDBC事务处理、封装JDBC工具类
MySQL数据库学习笔记(十)----JDBC事务处理.封装JDBC工具类 一.JDBC事务处理: 我们已经知道,事务的概念即:所有的操作要么同时成功,要么同时失败.在MySQL中提供了Commit. ...
- DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类)
DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类) 一.DAO模式简介 DAO即Data Access Object,数据访问接口.数据访问:故名思义就是与数据库打交道.夹在业务逻辑与数据 ...
- 转:轻松把玩HttpClient之封装HttpClient工具类(一)(现有网上分享中的最强大的工具类)
搜了一下网络上别人封装的HttpClient,大部分特别简单,有一些看起来比较高级,但是用起来都不怎么好用.调用关系不清楚,结构有点混乱.所以也就萌生了自己封装HttpClient工具类的想法.要做就 ...
- .NET3.5中JSON用法以及封装JsonUtils工具类
.NET3.5中JSON用法以及封装JsonUtils工具类 我们讲到JSON的简单使用,现在我们来研究如何进行封装微软提供的JSON基类,达到更加方便.简单.强大且重用性高的效果. 首先创建一个类 ...
- MySQL数据库学习笔记(十一)----DAO设计模式实现数据库的增删改查(进一步封装JDBC工具类)
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- MySQL数据库学习笔记(十)----JDBC事务处理、封装JDBC工具类
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- JAVA中封装JSONUtils工具类及使用
在JAVA中用json-lib-2.3-jdk15.jar包中提供了JSONObject和JSONArray基类,用于JSON的序列化和反序列化的操作.但是我们更习惯将其进一步封装,达到更好的重用. ...
随机推荐
- HDOJ 5402 Travelling Salesman Problem 模拟
行数或列数为奇数就能够所有走完. 行数和列数都是偶数,能够选择空出一个(x+y)为奇数的点. 假设要空出一个(x+y)为偶数的点,则必须空出其它(x+y)为奇数的点 Travelling Salesm ...
- TI C66x DSP 系统events及其应用 - 5.11(中断控制寄存器)
C66x DSP运行中断的简要流程: 1.使能了全局中断和子中断.假设硬件检測到中断发生,那么CPU就要跳转. 2.软件把CPU内部的A,B类寄存器的值等推入堆栈保存,把当前PC寄存器的值放入IRP寄 ...
- 喷水装置(一)(南阳oj6)(简单贪心)
喷水装置(一) 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描写叙述 现有一块草坪,长为20米.宽为2米.要在横中心线上放置半径为Ri的喷水装置.每一个喷水装置的效果都会让 ...
- android 使用post 提交
1.使用post 方式提交时不要把须要传递的參数写在URL 中,一定要使用 BasicNameValuePair 这个类来完毕 创建我想发送一个类似Get 方式的一个URL ---------- ht ...
- Tcl学习之--命名空间
Tcl解释器将全部的命令和全局变量分组管理.这些小组称为命名空间,一个命名空间中的命令和变量不会影响到还有一个命名空间.这些命名空间呈树形组织.一个命名空间的命令能够被还有一个命名空间引入.命名空间树 ...
- Silverlight访问数据库大全(转)
Silverlight访问数据库大全 Silverlight访问数据库大全 Posted on 2010-06-13 17:25 moss_tan_jun 阅读(1917) 评论(0) 编辑 收藏 最 ...
- bzoj4082
贪心+倍增 首先如果这个问题在序列上,好像可以按右端点排序,然后从起点开始向能到的最远的地方走. 但是环上不可以,因为随即一个起点可能不是最小的. 然后神思路来了:我们先将环展开倍增,再将区间按右端点 ...
- createrepo
[root@iio enp]# createrepo -g /enp/comps.xml .Spawning worker 0 with 1362 pkgsWorkers FinishedSaving ...
- Linux命令补充及基础优化。
1.用户部分 1.1 创建新用户 涉及命令 useradd [root@oldboyedu-50 ~]# useradd oldboy #添加用户 oldboy 1.2 设置密码 [root@oldb ...
- thinkphp结合云之讯做短信验证码
thinkphp结合云之讯做短信验证码先去云之讯注册账号 网址http://www.ucpaas.com/ 注册云之讯平台账号,即可免费获得10元测试费用测试够用啦 解压附件到 ThinkPHP\Li ...