matlab在运行一些大型程序时会比较慢,如果你的电脑正好有一张不错的显卡,那么为什么不用显卡来加速matlab运行呢?
本文将讲解如何使用gpu来加速matlab运行程序,并总结适合gpu加速的matlab程序。
准备工作:电脑上要有显卡,显卡要有cuda core。
目录
1. 认识你电脑的GPU
MATLAB指令为:gpuDevice
下面是我电脑上的gpu设备信息
>> gpuDevice
ans =
CUDADevice - 属性:
Name: 'NVIDIA RTX A2000 12GB'
Index: 1
ComputeCapability: '8.6'
SupportsDouble: 1
DriverVersion: 11.5000
ToolkitVersion: 11
MaxThreadsPerBlock: 1024
MaxShmemPerBlock: 49152
MaxThreadBlockSize: [1024 1024 64]
MaxGridSize: [2.1475e+09 65535 65535]
SIMDWidth: 32
TotalMemory: 1.2878e+10
AvailableMemory: 1.1272e+10
MultiprocessorCount: 26
ClockRateKHz: 1200000
ComputeMode: 'Default'
GPUOverlapsTransfers: 1
KernelExecutionTimeout: 1
CanMapHostMemory: 1
DeviceSupported: 1
DeviceAvailable: 1
DeviceSelected: 1
说明一下打印出来的信息的含义:
参数 | 含义 | 说明 |
---|---|---|
Compute Capability | 计算能力 | NVIDIA GPU Compute Capability解释_Forskamse的博客-CSDN博客 决定了GPU的通用规格和可用特性。Compute Capability的数值和GPU的计算速度无关,但是和GPU可执行的任务种类有关。 小数点前的第一位表示设备核心架构,小数点后的一位表示更加细微的进步,包括对核心架构的改进和功能的完善。 |
SupportsDouble | 是否支持双精度浮点运算 | 1表示支持 |
MaxThreadsPerBlock | 每个block中可开的最大线程数量 | |
MaxShmemPerBlock | 每个Block可用的最大共享内存大小 | |
MaxThreadBlockSize | 三个维度各自最大的线程数量 | |
MaxGridSize | 三个维度各自最大的Grid数量 | |
SIMDWidth | 同一条指令多个数据位宽 | 同时执行的线程数? |
MultiprocessorCount | streaming multiprocessors 数量 | |
ComputeMode | 模式 | default表示该设备不受限制,多个应用程序可以同时使用它。MATLAB 可以与其他应用程序(包括其他 MATLAB 会话或工作线程)共享设备。 |
GPUOverlapsTransfers | 是否支持覆盖传输 | |
KernelExecutionTimeout | 长时间运行的内核的超时标志 | |
CanMapHostMemory | 支持将主机内存映射到 CUDA 地址空间 | |
DeviceSupported | 本机Matlab支持的GPU设备数 | |
DeviceAvailable | 指示设备是否可用于当前 MATLAB 会话 | |
DeviceSelected | 当前选定设备的标志 |
附上一张GPU的架构图,便于理解
如果你能扫描出类似上面的结果,那么你就可以用gpu加速matlab程序啦。
2. 内存数据搬运
在正式开始加速之前,你还得明白一点,那就是数据的搬运。
如上图所示,正常来讲,matlab运行中的数据都是在内存里的,也就是电脑CPU的内存
如果要用GPU做计算,首先数据得在GPU的显存上
这就要研究下数据的搬运了:
GPU与显存位于显卡上,CPU位于电脑主板上,CPU与GPU之间通过PCIe连接
虽然PCIe已经是目前最快的接口了,但是毕竟是有速度上限的,所以当数据量多了以后必然传输数据是要时间的,这也是做加速必须要考虑的一个问题:
加速省下的时间能不能大于数传消耗的时间?
3. GPU加速举例
话不多说,直接上代码:
M = single(rand(7000,7000)); % 生成一个随机矩阵
tic
[A1,B1] = eig(M); % 求该随机矩阵的特征值和特征向量
t_cpu = toc
M = gpuArray(M); % 将数据从CPU中搬到GPU
tic
[A2,B2] = eig(M); % 求特征值和特征向量
t_gpu = toc
% A2 = gather(A2); % 将数据从GPU中搬到CPU
% B2 = gather(B2);
求一个较大矩阵的特征值和特征向量,分别运行在CPU和GPU上,并统计结果
怎么证明上面这段代码分别跑在了CPU和GPU上呢?很简单,打开任务管理器(win11):
点击任务管理器的性能标签栏,可以直观的看到CPU和GPU的利用率,CPU和GPU的利用率都依次跑到过100%
我们看看matlab打印的结果是不是gpu的时间比较短,下面是我电脑运行的结果,大家根据自己的电脑的性能适当的调节矩阵的大小。
可以看到GPU运行的速度是CPU的1.6倍。
再看一下matlab的工作区:
我们发现矩阵A2、B2、M的类型是gpuArray,代码中也调用了gpuArray()函数
所以GPU加速的关键就在于gpuArray
4. GPU加速方法
gpuArray是matlab自带的api函数,下面给出matlab官方的帮助文档:
Array stored on GPU - MATLAB - MathWorks 中国
这里总结几个常用的方法:
方法1:搬运数据 M2 = gpuArray(M1);
如第3节所示,讲位于内存的矩阵M1搬移到位于显存的矩阵M2
方法2:直接声明位于显存的数据 rand(7000,7000,'gpuArray');
支持这样直接创建gpuArray阵列的函数有300多个,若要查看,输入命令:methods('gpuArray')
方法3:让函数运行在GPU上 [A2,B2] = arrayfun(@eig,M);
arrayfun的加速效果没有前面两个好,gpu没有完全被调起来
另外与gpu有关的api函数还有一些:
函数 | 功能 |
gpuArray | 在GPU上创建阵列。 |
gather | 把分布式阵列或gpuArray转移到Matlab的workspace。 |
existsOnGPU | 确定gpuArray或CUDAKernel在GPU上可用。 |
gpuDevice | 查询或选择GPU设备。 |
gpuDeviceCount | 目前GPU的设备数量。 |
gputimeit | 在GPU上运行函数所需时间。 |
reset | 重置GPU设备并清空它的内存。 |
wait(GPUDevice) | 等待GPU计算完成。 |
arrayfun | 对GPU阵列上的每个元素应用函数。 |
bsxfun | 用于GPU阵列的二元但扩展函数。 |
pagefun | 对GPU阵列的每一页应用函数。 |
parallel.gpu.CUDAKernel | 从PTX和CU代码创建 GPU CUDA kernel 对象。 |
feval | 评估GPU的核。 |
setConstantMemory | 设置GPU的一些常量内存。 |
mxInitGPU | 这是一个C语言函数,在当前选择的设备上初始化Matlab GPU计算库。 |
可自行查阅matlab的帮助文档。
5. 适合GPU加速的程序
适合GPU加速的程序具有以下几个特点:
数据类型不是double。如果把第3节的代码的数据类型改为默认的double,gpu的加速效果就很一般了,我的机器上如果不转化float,cpu反而比gpu还要快(细心的读者可能注意到我任务管理器cpu的主频有4.5GHz,皮)
高度并行的。各个数据之间运算互不影响,耦合度较低。由于GPU本身硬件基础决定,各个workgroup之间并不相互通信,只有同一workgroup内的work-item之间才相互通信,所以GPU本身并不支持迭代等数据耦合度较高的计算,这是GPU本身要求。
数据量大的。用大量数据或者用同一数据多次调用同一公式或者计算过程,公式本身并不复杂,只是执行的次数较多。
接口交互少的。任务可以分为计算密集型和IO密集型。GPU适合计算密集型,即少量的IO读取+大量的计算;而IO密集型,是指多次使用IO读取+少量计算,这种情况涉及到内存与设显存之间的通信问题,主要限制原因是显存带宽问题。
可多个阶段执行的。运算程序可分解为多个小程序或者同一程序可分多个阶段执行,这就类似于使用集群处理同一任务,将其分解为多个任务碎片分发到各节点执行,以提高运算速率。