C++与CUDA
内存管理
封装
利用标准库容器实现对GPU的内存管理
#include <iostream>
#include <cuda_runtime.h>
#include <vector>
#include <cstddef>
template<class T>
struct CUDA_Allocator {
using value_type = T; //分配器必须要有的
T *allocate(size_t size) {
T *dataPtr = nullptr;
cudaError_t err = cudaMallocManaged(&dataPtr, size * sizeof(T));
if (err != cudaSuccess) {
return nullptr;
}
return dataPtr;
}
void deallocate(T *ptr, size_t size = 0) {
cudaError_t err = cudaFree(ptr);
}
};
__global__ void kernel(int *arr, int arrLen) {
for (int i = blockDim.x * blockIdx.x + threadIdx.x; i < arrLen; i += blockDim.x * gridDim.x) {
arr[i] = i;
//printf("i=%d\n", i);
}
}
int main() {
int size = 65523;
std::vector<int, CUDA_Allocator<int>> arr(size);
kernel<<<13, 28>>>(arr.data(), size);
cudaError_t err = cudaDeviceSynchronize();
if (err != cudaSuccess) {
printf("Error:%s\n", cudaGetErrorName(err));
return 0;
}
for (int i = 0; i < size; ++i) {
printf("arr[%d]=%d\n", i, arr[i]);
}
}
其中allocate
和deallocate
是必须实现的
这里不用默认的std::allocate,使用自己定义的分配器,使得内存分配在GPU上
vector是会自动初始化的,如果不想自动初始化的化,可以在分配器中自己写构造函数
关于分配器的更多介绍
函数调用
template<class Func>
__global__ void para_for(int n, Func func) {
for (int i = blockDim.x * blockIdx.x + threadIdx.x; i < n; i += blockDim.x * gridDim.x) {
func(i);
}
}
//定义一个仿函数
struct MyFunctor {
__device__ void operator()(int i) {
printf("number %d\n", i);
}
};
int main() {
int size = 65513;
para_for<<<13,33>>>(size,MyFunctor{});
cudaError_t err = cudaDeviceSynchronize();
if (err != cudaSuccess) {
printf("Error:%s\n", cudaGetErrorName(err));
return 0;
}
}
同样的,lambda也是被支持的,但是要先在cmake中开启
target_compile_options(${PROJECT_NAME} PUBLIC $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
lambda
lambda
写法
para_for<<<13, 33>>>(size, [] __device__(int i) { printf("number:%d\n", i); });
lambda
捕获外部变量
一定要注意深拷贝和浅拷贝
如果这里直接捕获arr的话,是个深拷贝,这样是会出错的,因为拿到的arr是在CPU上的,而数据是在GPU上的,所以这里要浅拷贝指针,拿到指针的值,就是数据在GPU上的地址,这样就可以使用device函数对数据进行操作了
std::vector<int, CUDA_Allocator<int>> arr(size);
int*arr_ptr=arr.data();
para_for<<<13, 33>>>(size, [=] __device__(int i) { arr_ptr[i] = i; });
cudaError_t err = cudaDeviceSynchronize();
if (err != cudaSuccess) {
printf("Error:%s\n", cudaGetErrorName(err));
return 0;
}
for (int i = 0; i < size; ++i) {
printf("arr[%d]=%d\n", i, arr[i]);
}
同时还可以这样捕获
para_for<<<13, 33>>>(size, [arr=arr.data()] __device__(int i) { arr[i] = i; });
时间测试
#include <chrono>
#define TICK(x) auto bench_##x = std::chrono::steady_clock::now();
#define TOCK(x) std::cout << #x ": " << std::chrono::duration_cast<std::chrono::duration<double> >(std::chrono::steady_clock::now() - bench_##x).count() << "s" << std::endl;
int main(){
int size = 65513;
std::vector<float, CUDA_Allocator<float>> arr(size);
std::vector<float> cpu(size);
TICK(cpu_sinf)
for (int i = 0; i < size; ++i) {
cpu[i] = sinf(i);
}
TOCK(cpu_sinf)
TICK(gpu_sinf)
para_for<<<16, 64>>>(
size, [arr = arr.data()] __device__(int i) { arr[i] = sinf(i); });
cudaError_t err = cudaDeviceSynchronize();
TOCK(gpu_sinf)
if (err != cudaSuccess) {
printf("Error:%s\n", cudaGetErrorName(err));
return 0;
}
}
结果:
可以看到,求正弦GPU是要快于CPU的,这里差距还不明显,一般来说速度是由数量级上的差距的
学习链接
© 版权声明
文章版权归作者所有,未经允许请勿转载。
版权声明:
1、IT大王遵守相关法律法规,由于本站资源全部来源于网络程序/投稿,故资源量太大无法一一准确核实资源侵权的真实性;
2、出于传递信息之目的,故IT大王可能会误刊发损害或影响您的合法权益,请您积极与我们联系处理(所有内容不代表本站观点与立场);
3、因时间、精力有限,我们无法一一核实每一条消息的真实性,但我们会在发布之前尽最大努力来核实这些信息;
4、无论出于何种目的要求本站删除内容,您均需要提供根据国家版权局发布的示范格式
《要求删除或断开链接侵权网络内容的通知》:https://itdw.cn/ziliao/sfgs.pdf,
国家知识产权局《要求删除或断开链接侵权网络内容的通知》填写说明: http://www.ncac.gov.cn/chinacopyright/contents/12227/342400.shtml
未按照国家知识产权局格式通知一律不予处理;请按照此通知格式填写发至本站的邮箱 wl6@163.com
1、IT大王遵守相关法律法规,由于本站资源全部来源于网络程序/投稿,故资源量太大无法一一准确核实资源侵权的真实性;
2、出于传递信息之目的,故IT大王可能会误刊发损害或影响您的合法权益,请您积极与我们联系处理(所有内容不代表本站观点与立场);
3、因时间、精力有限,我们无法一一核实每一条消息的真实性,但我们会在发布之前尽最大努力来核实这些信息;
4、无论出于何种目的要求本站删除内容,您均需要提供根据国家版权局发布的示范格式
《要求删除或断开链接侵权网络内容的通知》:https://itdw.cn/ziliao/sfgs.pdf,
国家知识产权局《要求删除或断开链接侵权网络内容的通知》填写说明: http://www.ncac.gov.cn/chinacopyright/contents/12227/342400.shtml
未按照国家知识产权局格式通知一律不予处理;请按照此通知格式填写发至本站的邮箱 wl6@163.com