设为首页 加入收藏

TOP

《CUDA编程:基础与实践》读书笔记(5):统一内存编程
2023-09-09 10:25:46 】 浏览:93
Tags:CUDA 编程

统一内存(unified memory)是一种逻辑上的概念,它既不是显存、也不是主机内存,而是CPU和GPU都可以访问并能保证一致性的虚拟存储器。使用统一内存对硬件有较高的要求:

  • 对于所有功能,GPU架构都必须不低于Kepler架构,主机应用程序必须为64位。
  • 对于一些较新的功能,至少需要Pascal架构的GPU,且主机必须是Linux系统。

使用统一内存编程的优势如下:

  • 统一内存使编程更加简单。不需要手动将数据在主机和设备之间传输,也不需要针对同一组数据定义两个指针并分别分配主机内存和设备内存。对于某个统一内存变量,可以直接从CPU或GPU中访问。
  • 可能会提供比手工移动数据更好的性能。统一内存的底层实现,可能会自动将一部分数据放到离某个处理器更近的位置,比如部分放到显存中,部分放到主存中,从而让相关数据尽量靠近对应的处理器。虽然统一内存机制可以部分地做到这些,但很多情况下还是需要手动给编译器一些提示,这部分功能需要Linux系统且不低于Pascal架构的GPU。
  • 允许GPU在使用统一内存的情况下,进行超量分配。超出GPU内存额度的部分可能存放在主机上。该功能也要求Linux系统且不低于Pascal架构的GPU。

动态分配统一内存使用的CUDA运行时API函数如下:

cudaError_t cudaMallocManaged(void **devPtr, size_t size, unsigned int flags = cudaMemAttachGlobal);

相比于cudaMalloc函数,该函数多个一个可选参数flags,其默认值cudaMemAttachGlobal代表分配的全局内存可由任何设备通过任何CUDA流进行访问。只能在主机端使用该函数分配统一内存,不能在核函数中使用该函数。分配了统一内存的变量既可以被设备访问,也可以被主机访问。从核函数的角度看,统一内存和普通设备内存在使用上没有任何区别,性能也基本相同。使用动态统一内存改写的数组相加例程如下:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <cmath>
#include <cstdio>

const double EPSILON = 1.0e-15;
const double a = 1.23;
const double b = 2.34;
const double c = 3.57;

void __global__ add(const double* x, const double* y, double* z)
{
    const int n = blockDim.x * blockIdx.x + threadIdx.x;
    z[n] = x[n] + y[n];
}

void check(const double* z, const int N)
{
    bool has_error = false;
    for (int n = 0; n < N; ++n)
    {
        if (fabs(z[n] - c) > EPSILON)
        {
            has_error = true;
        }
    }
    printf("%s\n", has_error ? "Has errors" : "No errors");
}

int main(void)
{
    const int N = 100000000;
    const int M = sizeof(double) * N;
    double* x, * y, * z;
    cudaMallocManaged((void**)&x, M);
    cudaMallocManaged((void**)&y, M);
    cudaMallocManaged((void**)&z, M);

    for (int n = 0; n < N; ++n)
    {
        x[n] = a;
        y[n] = b;
    }

    const int block_size = 128;
    const int grid_size = N / block_size;
    add << <grid_size, block_size >> > (x, y, z);

    cudaDeviceSynchronize();
    check(z, N);

    cudaFree(x);
    cudaFree(y);
    cudaFree(z);
    return 0;
}

统一内存也可以静态分配,只要在修饰符的__device__的基础上再加上修饰符__managed__即可。这样的变量是在任何函数外部定义的,可见范围是所在源文件(更准确地说是所在翻译单元)。官方文档《CUDA C++ Programming Guide》中的示例如下:

__device__ __managed__ int ret[1000];
__global__ void AplusB(int a, int b) {
    ret[threadIdx.x] = a + b + threadIdx.x;
}
int main() {
    AplusB<<< 1, 1000 >>>(10, 100);
    cudaDeviceSynchronize();
    for(int i = 0; i < 1000; i++)
        printf("%d: A+B = %d\n", i, ret[i]);
    return 0;
}
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇1.1QT网络通信 下一篇乘法逆元及其三种求法

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目