系列文章目录
前言
这里开始跟大家分享CUDA内存管理相关的知识。
一、内存管理相关概念
这里主要介绍如何使用CUDA函数来显示管理内存和数据移动。
CUDA提供了在主机端准备设备内存的函数,并且显示的向设备传输数据和从设备中获取数据。
1. 内存分配和释放
在主机上使用如下函数分配全局内存:
c
u
d
a
E
r
r
o
r
t
c
u
d
a
M
a
l
l
o
c
(
v
o
i
d
∗
∗
d
e
v
P
t
r
,
s
i
z
e
_
t
c
o
u
n
t
)
;
cudaError_t cudaMalloc(void**\ devPtr, size\_t\ count);
cudaErrortcudaMalloc(void∗∗ devPtr,size_t count);
c
u
d
a
M
a
l
l
o
c
cudaMalloc
cudaMalloc函数执行失败则返回
c
u
d
a
E
r
r
o
r
M
e
m
o
r
y
A
l
l
o
c
a
t
i
o
n
cudaErrorMemoryAllocation
cudaErrorMemoryAllocation,在已分配的全局内存中的值不会被清除,需要从主机上传输的数据来填充所分配的全局内存,或用下列函数将其初始化:
c
u
d
a
E
r
r
o
r
t
c
u
d
a
M
e
m
s
e
t
(
v
o
i
d
∗
d
e
v
P
t
r
,
i
n
t
v
a
l
u
e
,
s
i
z
e
_
t
c
o
u
n
t
)
;
cudaError_t cudaMemset(void\ *devPtr, int\ value, size\_t\ count);
cudaErrortcudaMemset(void ∗devPtr,int value,size_t count);
一旦已分配的设备全局内存不再被使用,可用如下函数释放该内存空间:
c
u
d
a
E
r
r
o
r
t
c
u
d
a
F
r
e
e
(
v
o
i
d
∗
d
e
v
P
t
r
)
;
cudaError_t cudaFree(void\ *devPtr);
cudaErrortcudaFree(void ∗devPtr);
该函数释放的内存必须由 c u d a M a l l o c cudaMalloc cudaMalloc函数分配,否则,将返回一个错误 c u d a E r r o r I n v a l i d D e v i c e P o i n t e r cudaErrorInvalidDevicePointer cudaErrorInvalidDevicePointer;如果地址空间已被释放, c u d a F r e e cudaFree cudaFree函数也返回一个错误。
这里有一点要特别注意,设备内存的分配和释放操作成本较高,所以应用程序应该重利用设备内存,以减少对整体性能的影响。
2. 内存传输
一旦全局内存分配好了,可用如下函数从主机向设备传输数据:
c
u
d
a
E
r
r
o
r
_
t
c
u
d
a
M
e
m
c
p
y
(
v
o
i
d
∗
d
s
t
,
c
o
n
s
t
v
o
i
d
∗
s
r
c
,
s
i
z
e
_
t
c
o
u
n
t
,
e
n
u
m
c
u
d
a
M
e
m
c
p
y
K
i
n
d
k
i
n
d
)
;
cudaError\_t\ cudaMemcpy(void\ *dst, const\ void\ *src,\ size\_t\ count, enum\ cudaMemcpyKind\ kind);
cudaError_t cudaMemcpy(void ∗dst,const void ∗src, size_t count,enum cudaMemcpyKind kind);
这个函数从内存位置src复制count字节到内存位置dst。kind指定了复制方向,取值如下:
c
u
d
a
M
e
m
c
p
y
H
o
s
t
T
o
H
o
s
t
cudaMemcpyHostToHost
cudaMemcpyHostToHost
c
u
d
a
M
e
m
c
p
y
H
o
s
t
T
o
D
e
v
i
c
e
cudaMemcpyHostToDevice
cudaMemcpyHostToDevice
c
u
d
a
M
e
m
c
p
y
D
e
v
i
c
e
T
o
H
o
s
t
cudaMemcpyDeviceToHost
cudaMemcpyDeviceToHost
c
u
d
a
M
e
m
c
p
y
D
e
v
i
c
e
T
o
D
e
v
i
c
e
cudaMemcpyDeviceToDevice
cudaMemcpyDeviceToDevice
如果指针dst和src与kind指定的复制方向不一致,那么 c u d a M e m c p y cudaMemcpy cudaMemcpy函数的行为就是未定义行为,该函数在大多数情况下都是同步的。
下图显示了从GPU内存与CPU内存间的连接性能。从图中可以看出,GPU芯片和板载GDDR5 GPU内存间的理论峰值带宽非常高,CPU和GPU间通过PCIe Gen2总线相连,这种连接的理论带宽要低得多。因此,主机和设备间的数据传输会降低应用程序的整体性能。
CUDA编程的一个基本原则:应尽可能地减少主机与设备间的传输。
二、内存分配、传输与释放示例
#include <iostream>
#include <cuda.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <device_functions.h>
int main()
{
//set up device
int dev = 0;
cudaSetDevice(dev);
//memory size
unsigned int isize = 1 << 22;
unsigned int nbytes = isize * sizeof(float);
//get device information
cudaDeviceProp stDeviceProp;
cudaGetDeviceProperties(&stDeviceProp, dev);
printf("starting at ");
printf("device %d: %s memory size %d nbyte %5.2fMB\n", dev,
stDeviceProp.name, isize, nbytes / (1024.0f * 1024.0f));
//allocate the host memory
float* h_a = (float*)malloc(nbytes);
//allocate the device memory
float* d_a;
cudaMalloc(&d_a, nbytes);
//initialize the host memory
for (unsigned int i = 0; i < isize; i++)
h_a[i] = 0.5f;
//transfer data from the host to the device
cudaMemcpy(d_a, h_a, nbytes, cudaMemcpyHostToDevice);
//tansfer data from the device to the host
cudaMemcpy(h_a, d_a, nbytes, cudaMemcpyDeviceToHost);
//free memory
cudaFree(d_a);
free(h_a);
//reset device
cudaDeviceReset();
system("pause");
return 0;
}
总结
这篇内容比较基础,算是入门级别,很适合向本人这样的小白,希望对大家有用!
参考资料
《CUDA C编程权威指南》