第四天 cuda高效的内存分配(含代码)

cuda高效的内存分配

统一内存(Unified addressing)

统一内存是CUDA编程模型的一个组成部分,CUDA 6.0首次介绍了该模型

它定义了一个托管(managed)内存空间,其中所有处理器(包括CPU和GPU)都可以看到共同的地址空间。

通过让底层系统自己管理CUDA数据访问和位置,避免显式数据拷贝,这主要带来两方面好处:

  1. 简化编程
  2. 通过透明地将数据迁移到使用它的处理器,可以最大限度地提高数据访问速度

设备是否支持统一内存可以通过一下代码查询

//如果支持,unifiedAddressing字段为1
int deviceCount;
cudaGetDeviceCount(&deviceCount);
int device;
for (device = 0; device < deviceCount; ++device) {
    cudaDeviceProp deviceProp;
    cudaGetDeviceProperties(&deviceProp, device);
    printf("Device %d has compute capability %d.%d.\n",
           device, deviceProp.major, deviceProp.minor);
}

当应用程序是64位进程,并且主机和所有具有计算能力2.0(附录说的是3.0以上)及更高版本的设备都使用一个地址空间

此时通过CUDA API分配的所有主机内存和所有受支持设备上开辟的设备内存都在此虚拟地址范围内

通过CUDA接口分配的主机内存或者任何使用统一地址空间的设备分配的设备内存都可以使用cudaPointerGetAttributes()的指针来确定地址。

使用统一内存进行数据拷贝不需要执行拷贝类型,只需使用cudaMemcpyDefault,这同样适用于不使用CUDA API分配的主机内存,只要设备使用的是统一地址

如果使用统一地址空间,通过cudaHostAlloc()分配的页锁定主机内存会默认cudaHostAllocPortable,此时指针可以直接在内核函数使用,而无需像页锁定内存那样先通过cudaHostGetDevicePointer()函数获取设备指针


分配线性内存

cuMemAlloc()
线性内存被组织在单个连续的地址空间中,可以直接以及线性地访问这些内存位置。
内存分配空间以字节为大小,并返回所分配的内存地址
在这里插入图片描述


分配主机锁页内存

cuMemAllocHost()
在这里插入图片描述
锁页内存:页面不允许被调入调出的叫锁页内存,反之叫可分页内存。

优点:

  1. 速度快
  2. 设备可以直接访问内存,与可分页内存相比,它的读写带宽要高得多
  3. 驱动程序会跟踪使用cuMemAllocHost()分配的虚拟内存范围,并自动加速对cuMemcpy()等函数的调用

缺点:

  1. 分配过多锁业内存会减少系统可用于分页的内存量,可能会降低系统性能

  2. 在主机和设备之间为数据交换分配临时区域时,最好少用此功能。

    paged-memory.jpg


内存的初始化

cuMemsetD32(CUdeviceptr dstDevice, unsigned int ui, size_t N)

在这里插入图片描述


内存的释放

cuMemFreeHost()

在这里插入图片描述


代码示例

// CUDA驱动头文件cuda.h
#include <cuda.h>

#include <stdio.h>
#include <string.h>

#define checkDriver(op)  __check_cuda_driver((op), #op, __FILE__, __LINE__)

bool __check_cuda_driver(CUresult code, const char* op, const char* file, int line){

    if(code != CUresult::CUDA_SUCCESS){    
        const char* err_name = nullptr;    
        const char* err_message = nullptr;  
        cuGetErrorName(code, &err_name);    
        cuGetErrorString(code, &err_message);   
        printf("%s:%d  %s failed. \n  code = %s, message = %s\n", file, line, op, err_name, err_message);   
        return false;
    }
    return true;
}

int main(){

    // 检查cuda driver的初始化
    checkDriver(cuInit(0));

    // 创建上下文
    CUcontext context = nullptr;
    CUdevice device = 0;
    checkDriver(cuCtxCreate(&context, CU_CTX_SCHED_AUTO, device));
    printf("context = %p\n", context);

    // 输入device prt向设备要一个100 byte的线性内存,并返回地址
    CUdeviceptr device_memory_pointer = 0;
    checkDriver(cuMemAlloc(&device_memory_pointer, 100)); // 注意这是指向device的pointer, 
    printf("device_memory_pointer = %p\n", device_memory_pointer);

    // 输入二级指针向host要一个100 byte的锁页内存,专供设备访问
    float* host_page_locked_memory = nullptr;
    checkDriver(cuMemAllocHost((void**)&host_page_locked_memory, 100));
    printf("host_page_locked_memory = %p\n", host_page_locked_memory);

    // 向page-locked memory 里放数据(仍在CPU上),可以让GPU可快速读取
    host_page_locked_memory[0] = 123;
    printf("host_page_locked_memory[0] = %f\n", host_page_locked_memory[0]);
    /* 
        host page locked memory 声明的时候为float*型,可以直接转换为device ptr
        这才可以送给cuda核函数(利用DMA(Direct Memory Access)技术)
        
        初始化内存的值: cuMemsetD32 ( CUdeviceptr dstDevice, unsigned int  ui, size_t N )
        初始化值必须是无符号整型,因此需要将new_value进行数据转换:
        但不能直接写为:(int)value,必须写为*(int*)&new_value, 我们来分解一下这条语句的作用:
        1. &new_value获取float new_value的地址
        (int*)将地址从float * 转换为int*以避免64位架构上的精度损失
        *(int*)取消引用地址,最后获取引用的int值
     */
    
    float new_value = 555;
    checkDriver(cuMemsetD32((CUdeviceptr)host_page_locked_memory, *(int*)&new_value, 1)); 
    printf("host_page_locked_memory[0] = %f\n", host_page_locked_memory[0]);

    // 释放内存
    checkDriver(cuMemFreeHost(host_page_locked_memory));
    return 0;
}

参考文献

  1. https://developer.nvidia.com/cuda-toolkit-archive
  2. https://docs.nvidia.com/cuda/archive/11.2.0/
  3. https://developer.nvidia.com/blog/unified-memory-cuda-beginners/
  4. https://leimao.github.io/blog/Page-Locked-Host-Memory-Data-Transfer/
  5. https://www.youtube.com/watch?v=p9yZNLeOj4s
  6. https://blog.csdn.net/Geek_/article/details/104359596
  7. https://developer.download.nvidia.cn/compute/DevZone/docs/html/C/doc/html/
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鹏AI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值