主要内容

GPU内存分配和最小化

离散模式和管理模式

GPU编码器™为您提供两种不同的内存分配(malloc)模式可在CUDA®编程模型,cudaMalloccudaMallocManagedcudaMallocAPI适用于传统的独立CPU和GPU全局内存。cudaMallocManaged适用于统一内存

从程序员的角度来看,传统的计算机体系结构要求在CPU和GPU内存空间之间分配和共享数据。应用程序需要管理这两个内存空间之间的数据传输,这增加了复杂性。统一内存创建一个托管内存池,在CPU和GPU之间共享。CPU和GPU都可以通过一个指针访问托管内存。统一内存试图通过将数据迁移到需要它的设备来优化内存性能,同时对程序隐藏迁移细节。虽然统一内存简化了编程模型,但当写入GPU的数据在CPU上被访问时,需要进行设备同步调用。GPU编码器插入这些同步调用。根据英伟达®,统一内存可以提供显著的性能优势,当使用CUDA 8.0,或针对嵌入式硬件,如NVIDIA Tegra®

如果需要在GPU编码器应用程序中更改内存分配模式,请使用Malloc模式下的下拉框设置- > GPU编码器.当使用命令行界面时,使用MallocMode生成配置属性,并将其设置为任意“离散”“统一”

内存极小化

GPU编码器分析CPU和GPU分区之间的数据依赖关系,并进行优化,使CPU和GPU分区的数量最小化cudaMemcpy函数调用生成的代码。该分析还确定了数据必须在CPU和GPU之间复制的最小位置集cudaMemcpy

例如,函数喷火有一段代码在CPU上顺序处理数据,在GPU上并行处理数据。

function [out] = foo(input1,input2)…% CPU work input1 =…input2 =…tmp1 = ... ... % GPU work kernel1(gpuInput1, gpump1);kernel2 (gpuInput2 gpuTmp1 gpuTmp2);kernel3 (gpuTmp1 gpuTmp2 gpuOut);% CPU工作…= out结束

一个未优化的CUDA实现可能有多个cudaMemcpy函数调用来传输所有输入gpuInput1, gpuInput2,以及暂时的结果gpuTmp1, gpuTmp2内核调用之间。因为中间结果gpuTmp1, gpuTmp2不在GPU之外使用,它们可以存储在GPU内存中导致更少cudaMemcpy函数调用。这些优化提高了生成代码的整体性能。优化的实现是:

gpuInput1 = input1;gpuInput2 = input2;kernel1 < < < > > > (gpuInput1 gpuTmp1);kernel2<< >>>(gpuInput2, gpuTmp1, gpuTmp2);kernel3<< >>>(gpuTmp1, gpuTmp2, gpuOut);= gpuOut;

为了消除冗余cudaMemcpy调用时,GPU编码器分析给定变量的所有使用和定义,并使用状态标志执行最小化。这个表中显示了原始代码的示例和生成的代码。

原始代码 优化生成的代码
A(:) = ... ... for i = 1:N gB = kernel1(gA);gA = kernel2 (gB);如果(somecondition) gC = kernel3(gA, gB);end…end ... ... = C;
A(:) =…A_isDirtyOnCpu = true;for i = 1:N if (A_isDirtyOnCpu) gA = A;A_isDirtyOnCpu = false;end gB = kernel1(gA);gA = kernel2 (gB);如果(somecondition) gC = kernel3(gA, gB);C_isDirtyOnGpu = true;end…end…if (C_isDirtyOnGpu) C = gC;C_isDirtyOnGpu = false;= C;

_isDirtyOnCpu标志告诉GPU编码器内存优化的例程,给定变量在CPU或GPU上声明和使用。