文档gydF4y2Ba

测量和改进GPU性能gydF4y2Ba

GPU基准测试入门gydF4y2Ba

您可以在MATLAB中使用各种基准测试gydF4y2Ba®gydF4y2Ba来衡量GPU的性能:gydF4y2Ba

  • 使用gydF4y2BagpuBenchgydF4y2Ba在MATLAB中央文件交换中进行各种测试,包括单精度和双精度的内存和计算密集型任务。比较显示卡和计算卡的性能。有关更多信息,请参见gydF4y2Ba//www.tianjin-qmedu.com/matlabcentral/fileexchange/34080-gpubenchgydF4y2Ba.gydF4y2Ba

  • 使用gydF4y2Baparalleldemo_gpu_benchgydF4y2Ba脚本gydF4y2BaGPU性能测试gydF4y2Ba获取有关PCI总线速度、GPU内存读/写和峰值计算性能的信息,用于双精度矩阵计算。gydF4y2Ba

使用单一精度计算提高性能gydF4y2Ba

您可以通过单精度而不是双精度进行计算来提高GPU的性能。另一方面,在CPU计算中,当从双精度切换到单精度时,您不会得到这种改进。原因是大多数GPU卡是为图形显示设计的,对单精度性能要求很高。gydF4y2Ba

适合在GPU上进行单精度计算的典型例子包括图像处理和机器学习。gydF4y2Ba//www.tianjin-qmedu.com/deep-learning-in-the-cloud-with-matlab-white-papergydF4y2Ba.然而,其他类型的计算,如线性代数问题,通常需要双精度处理。gydF4y2Ba

与双精度计算相比,单精度计算的性能最多可以提高50倍,这取决于GPU卡和核总数。高端计算卡通常表现出较小的改善。您可以通过使用确定特定GPU的性能改进gydF4y2BagpuBenchgydF4y2Ba,请参阅gydF4y2Ba//www.tianjin-qmedu.com/matlabcentral/fileexchange/34080-gpubenchgydF4y2Ba.gydF4y2Ba

获取NVIDIA的全面性能概述gydF4y2Ba®gydF4y2BaGPU卡,请参见gydF4y2Bahttps://en.wikipedia.org/wiki/List_of_Nvidia_graphics_processing_unitsgydF4y2Ba.单精度和双精度之间的性能提升因子计算方法如下:gydF4y2Ba

  • 在上面的维基页面上找到GPU。gydF4y2Ba

  • 从表中获取指定的单精度和双精度性能值。如果没有双精度GFLOPS值,则假设双精度的比率为24‐32倍。gydF4y2Ba

  • 将指定的单精度GFLOPS值除以双精度GFLOPS值。gydF4y2Ba

请注意gydF4y2Ba

如果你的笔记本电脑中有移动显卡,你可以使用这张卡进行GPU计算。然而,笔记本电脑的GPU可能远不如台式机的GPU强大,因此性能下降。gydF4y2Ba

提高性能的基本工作流程gydF4y2Ba

MATLAB中GPU计算的目的是加速您的应用程序。本主题讨论可以帮助您在GPU上实现更好性能的基本概念和实践,例如GPU硬件的配置和代码中的最佳实践。它讨论了实现难度和性能之间的权衡,并描述了在使用gpuArray函数、gydF4y2BaarrayfungydF4y2Ba、墨西哥文件或CUDA内核。最后,介绍了如何准确地测量GPU的性能。gydF4y2Ba

当将MATLAB代码转换为在GPU上运行时,最好从性能已经良好的MATLAB代码开始。虽然GPU和CPU有不同的性能特征,但编写良好MATLAB代码的一般准则也有助于为GPU编写良好的MATLAB代码。第一步几乎总是分析CPU代码。剖析器显示在CPU上花费时间最多的代码行很可能是为GPU编写代码时必须集中精力处理的代码行。gydF4y2Ba

使用支持gpuArray数据的MATLAB内置函数最容易开始转换代码。万博1manbetx这些函数接受gpuArray输入,在GPU上执行计算,并返回gpuArray输出。中可以找到支持gpuArray数据的MATLAB函数列表万博1manbetxgydF4y2Ba在GPU上运行内置函数gydF4y2Ba.一般来说,这些函数支持与在CPU中计算的标准MATLA万博1manbetxB函数相同的参数和数据类型。这些函数对gpuArrays的任何限制都在它们的命令行帮助中描述(例如,gydF4y2Ba帮助gpuArray / qrgydF4y2Ba).gydF4y2Ba

如果GPU支持您想使用的所有函数,那么在GPU上运行代码可能就像调用一样简单万博1manbetxgydF4y2BagpuArraygydF4y2Ba将输入数据传输到GPU,并调用gydF4y2Ba收集gydF4y2Ba完成后从GPU检索输出数据。在许多情况下,您可能需要对代码进行向量化,用MATLAB矩阵和向量运算替换循环标量运算。虽然向量化在CPU上通常是一种很好的实践,但它对于在GPU上实现高性能通常是至关重要的。有关更多信息,请参见gydF4y2Ba矢量化以改进GPU性能gydF4y2Ba.gydF4y2Ba

提高性能的先进工具gydF4y2Ba

即使在将输入转换为gpuArrays并对代码进行向量化之后,算法中的某些操作可能不是内置函数,或者速度不够快,无法满足应用程序的需求。在这种情况下,你有三个主要的选择:使用gydF4y2BaarrayfungydF4y2Ba预编译应用程序的元素部分,使用GPU库函数,或编写自定义CUDA内核。gydF4y2Ba

如果您有一个纯元素的函数,您可以通过调用来提高它的性能gydF4y2BaarrayfungydF4y2Ba.的gydF4y2BaarrayfungydF4y2Ba函数将一个基于元素的MATLAB函数转换为自定义CUDA内核,从而减少了执行操作的开销。通常情况下,可以使用应用程序的一个子集gydF4y2BaarrayfungydF4y2Ba即使整个应用程序不能。这个例子gydF4y2Ba利用ARRAYFUN提高基于元素的MATLAB函数在GPU上的性能gydF4y2Ba展示了这种方法的基本概念;这个例子gydF4y2Ba使用ARRAYFUN进行蒙特卡罗模拟gydF4y2Ba演示如何在金融应用程序的模拟中完成这一点。gydF4y2Ba

MATLAB在并行计算工具箱™、图像处理工具箱™、信号处理工具箱™和其他产品中提供了广泛的gpu支持函数库。s manbetx 845然而,有许多附加函数库在MATLAB的GPU支持中没有直接的内置类似物。万博1manbetx示例包括NVIDIA Performance Primitives库和CURAND库,它们包含在MATLAB附带的CUDA工具包中。如果需要调用其中一个库中的函数,可以使用GPU MEX接口。这个接口允许您从MATLAB gpuArrays中提取到设备数据的指针,以便您可以将这些指针传递给GPU函数。您可以将返回值转换为返回到MATLAB的gpuArrays。有关更多信息,请参阅gydF4y2Ba运行包含CUDA代码的MEX-FunctionsgydF4y2Ba.gydF4y2Ba

最后,您可以选择为所需的操作编写定制CUDA内核。这样的内核可以使用CUDAKernel对象直接集成到MATLAB中。gydF4y2Ba

这个例子gydF4y2Ba阐述GPU计算的三种方法:Mandelbrot集gydF4y2Ba演示如何使用本节中提到的三种方法实现简单的计算。本例从易于转换为在GPU上运行的MATLAB代码开始,重写代码以供使用gydF4y2BaarrayfungydF4y2Ba为元素操作,并最后展示了如何集成一个自定义CUDA内核为相同的操作。gydF4y2Ba

另外,你也可以把CUDA内核写在MEX-file中,然后在MEX-file中使用CUDA运行时API来调用它。这两种方法中的任何一种都可以让您使用GPU的底层特性,例如共享内存和纹理内存,这些特性在MATLAB代码中无法直接获得。更多详细信息,请参见示例gydF4y2Ba使用MEX访问高级CUDA功能gydF4y2Ba.gydF4y2Ba

提高性能的最佳实践gydF4y2Ba

硬件配置gydF4y2Ba

一般来说,当GPU专用于计算时,您可以实现最佳性能。对于计算和图形来说,使用相同的GPU设备通常是不实际的,因为合理大小的问题会占用大量的内存,而且系统会不断地使用设备来处理图形。如果可能的话,获取一个单独的图形设备。为计算或图形配置设备的详细信息取决于操作系统和驱动程序版本。gydF4y2Ba

在Windows上gydF4y2Ba®gydF4y2Ba在系统中,GPU设备可以处于两种模式之一:Windows显示驱动程序模型(WDDM)或特斯拉计算集群(TCC)模式。为了获得最佳性能,任何用于计算的设备都应该处于TCC模式。有关更多详细信息,请参阅NVIDIA文档。gydF4y2Ba

NVIDIA最高性能的计算设备Tesla系列在读取和写入GPU内存时支持纠错码(ECC)。万博1manbetxECC的目的是在读写动态内存时纠正偶尔发生的位错误。一种提高性能的技术是关闭ECC以增加可达到的内存带宽。虽然硬件可以这样配置,但MathWorks不建议这样做。无声错误导致的潜在精度损失可能比性能好处更有害。gydF4y2Ba

MATLAB编码实践gydF4y2Ba

本节介绍提升GPU性能的通用技术。其中一些技巧也适用于为CPU编写MATLAB代码。gydF4y2Ba

MATLAB数组中的数据以列为主顺序存储。因此,沿着数组的第一个维度或列维度进行操作是有益的。如果数据的一个维度比其他维度长得多,那么将其作为第一个维度可能会获得更好的性能。类似地,如果经常沿着某个特定维度操作,通常最好将其作为第一个维度。在某些情况下,如果连续操作的目标是数组的不同维度,那么在这些操作之间对数组进行转置或排列可能是有益的。gydF4y2Ba

gpu通过并行计算多个结果来实现高性能。因此,矩阵和高维数组操作通常比对向量或标量的操作执行得更好。通过重写循环以使用高维操作,可以获得更好的性能。修改基于循环的、面向标量的代码以使用MATLAB矩阵和矢量操作的过程称为向量化。详情请参见gydF4y2Ba使用向量化gydF4y2Ba(MATLAB)。gydF4y2Ba

默认情况下,MATLAB中的所有操作都是用双精度浮点算法执行的。然而,大多数操作支持多种数据类型,包括整数和单精度万博1manbetx浮点数。今天的gpu和cpu在执行单精度操作时通常具有更高的吞吐量,而单精度浮点数据占用的内存更少。如果应用程序的精度要求允许使用单精度浮点数,则可以极大地提高MATLAB代码的性能。gydF4y2Ba

GPU位于被称为PCI总线的数据传输机制的末端。虽然该总线是一种有效的、高带宽的方式,可以将数据从PC主机内存传输到各种扩展卡,但它仍然比GPU设备或CPU的全局内存的总体带宽慢得多(有关更多细节,请参阅示例)gydF4y2BaGPU性能测试gydF4y2Ba).此外,从GPU设备到MATLAB主机内存的传输导致MATLAB在执行任何其他语句之前等待设备上所有挂起的操作完成。这可能会严重损害应用程序的性能。一般来说,应该限制在MATLAB工作区和GPU之间传输数据的次数。如果可以在应用程序开始时将数据传输到GPU,在GPU上执行所有计算,然后在结束时将结果传输回MATLAB,通常会产生最佳性能。类似地,如果可能的话,它有助于直接在GPU上创建数组,使用gydF4y2Ba“gpuArray”gydF4y2Ba或者是gydF4y2Ba“喜欢”gydF4y2Ba等函数的选项gydF4y2Ba0gydF4y2Ba(例如,gydF4y2BaZ = 0 (___,'gpuArray')gydF4y2Ba或gydF4y2BaZ = 0 (N,'like',g)gydF4y2Ba对于现有的gpuArraygydF4y2BaggydF4y2Ba).gydF4y2Ba

GPU性能测试gydF4y2Ba

衡量GPU性能的最佳方法是使用gydF4y2BagputimeitgydF4y2Ba.该函数接受一个不带输入参数的函数句柄作为输入,并返回该函数的测量执行时间。它考虑了诸如重复计时操作以获得更好的分辨率、在测量之前执行函数以避免初始化开销以及减去计时函数的开销等基准测试方面的考虑。同时,gydF4y2BagputimeitgydF4y2Ba确保GPU上的所有操作都在最终计时之前完成。gydF4y2Ba

例如,考虑测量计算的时间gydF4y2Ba陆gydF4y2Ba随机矩阵的因式分解gydF4y2Ba一个gydF4y2Ba的大小gydF4y2BaNgydF4y2Ba——- - - - - -gydF4y2BaNgydF4y2Ba.可以通过定义一个函数来实现gydF4y2Ba陆gydF4y2Ba分解并将函数句柄传递给gydF4y2BagputimeitgydF4y2Ba:gydF4y2Ba

A = rand(N,gydF4y2Ba“gpuArray”gydF4y2Ba);fh = @() lu(A);gputimeit (fh, 2);gydF4y2Ba%第二个参数表示输出数量gydF4y2Ba

您还可以使用gydF4y2Ba抽搐gydF4y2Ba而且gydF4y2BatocgydF4y2Ba.然而,为了在GPU上获得准确的计时,你必须等待操作完成后再调用gydF4y2BatocgydF4y2Ba.有两种方法可以做到这一点。你可以打电话gydF4y2Ba收集gydF4y2Ba在调用之前在最终的GPU输出上gydF4y2BatocgydF4y2Ba:这将迫使所有计算在时间测量之前完成。或者,您可以使用gydF4y2Ba等待gydF4y2Ba函数使用gydF4y2BaGPUDevicegydF4y2Ba对象作为输入。例如,如果您想测量计算的时间gydF4y2Ba陆gydF4y2Ba矩阵的分解gydF4y2Ba一个gydF4y2Ba使用gydF4y2Ba抽搐gydF4y2Ba,gydF4y2BatocgydF4y2Ba,gydF4y2Ba等待gydF4y2Ba,你可以这样做:gydF4y2Ba

gd = gpuDevice();抽搐();[l,u] = lu(A);等待(gd);tLU = toc();gydF4y2Ba

您还可以使用MATLAB分析器来显示计算时间在GPU代码中的分布情况。注意,为了完成计时测量,分析器要独立地运行每一行代码,因此它不能考虑在正常操作期间可能发生的重叠(异步)执行。对于整个算法的计时,您应该使用gydF4y2Ba抽搐gydF4y2Ba而且gydF4y2BatocgydF4y2Ba,或gydF4y2BagputimeitgydF4y2Ba,如上所述。此外,如果用户定义的MEX函数异步运行,概要文件可能不会为它们产生正确的结果。gydF4y2Ba

矢量化以改进GPU性能gydF4y2Ba

这个例子向您展示了如何通过在GPU而不是CPU上运行函数来提高性能,并通过向量化计算。gydF4y2Ba

考虑一个对矩阵列执行快速卷积的函数。快速卷积是信号处理应用中常见的操作,它将每一列数据从时域变换到频域,再乘以滤波向量的变换,再变换回时域,并将结果存储在输出矩阵中。gydF4y2Ba

函数gydF4y2Bay = fastConvolution(data,filter) [m,n] = size(data);gydF4y2Ba零垫过滤到列长度的数据,并进行转换gydF4y2BaFilter_f = fft(filter,m);gydF4y2Ba创建一个与data大小和类别相同的零数组gydF4y2BaY = 0 (m,n,gydF4y2Ba“喜欢”gydF4y2Ba、数据);gydF4y2Ba转换数据的每一列gydF4y2Ba为gydF4y2BaIx = 1:n af = fft(data(:, Ix));Y (:,ix) = ifft(af .* filter_f);gydF4y2Ba结束gydF4y2Ba结束gydF4y2Ba

在CPU中对特定大小的数据执行此函数,并使用MATLAB测量执行时间gydF4y2Ba时间gydF4y2Ba函数。的gydF4y2Ba时间gydF4y2Ba函数负责常见的基准测试考虑事项,例如计算启动和开销。gydF4y2Ba

A = complex(randn(4096,100),randn(4096,100));gydF4y2Ba数据输入gydF4y2BaB = randn(16,1);gydF4y2Ba%过滤器输入gydF4y2Bac = fastConvolution(a,b);gydF4y2Ba计算输出gydF4y2Bactime = timeit(@()fastConvolution(a,b));gydF4y2Ba测量CPU时间gydF4y2Badisp ([gydF4y2Ba' CPU上的执行时间= 'gydF4y2Banum2str (ctime)]);gydF4y2Ba

在样例机器上,以下代码显示输出:gydF4y2Ba

CPU上的执行时间= 0.019335gydF4y2Ba

现在在GPU上执行这个函数。通过将输入数据更改为gpuArrays而不是普通的MATLAB数组,可以很容易地做到这一点。的gydF4y2Ba“喜欢”gydF4y2Ba在函数内部创建输出时使用的语法确保gydF4y2BaygydF4y2Ba将是一个gpuArray如果gydF4y2Ba数据gydF4y2Ba是一个gpuArray。gydF4y2Ba

ga = gpuArray(a);gydF4y2Ba移动数组到GPUgydF4y2Bagb = gpuArray(b);gydF4y2Ba移动过滤器到GPUgydF4y2Bagc = fastConvolution(ga,gb);gydF4y2Ba% GPU计算gydF4y2Bagtime = gputimeit(@()fastConvolution(ga,gb));gydF4y2Ba测量GPU时间gydF4y2BaGerr = max(max(abs(gather(gc)-c)));gydF4y2Ba%计算错误gydF4y2Badisp ([gydF4y2Ba' GPU上的执行时间= 'gydF4y2Banum2str (gtime)]);disp ([gydF4y2Ba'最大绝对误差= 'gydF4y2Banum2str (gerr)]);gydF4y2Ba

在同一台机器上,下面的代码显示输出:gydF4y2Ba

CPU执行时间= 0.019335 GPU执行时间= 0.027235最大绝对误差= 1.1374e-14gydF4y2Ba

不幸的是,对于这个问题,GPU比CPU慢。原因是gydF4y2Ba为gydF4y2Ba-loop对长度为4096的列执行FFT、乘法和反FFT操作。提高性能的最佳方法是对代码进行向量化,这样单个MATLAB函数调用就可以执行更多的计算。FFT和IFFT操作很容易向量化:gydF4y2Bafft (A)gydF4y2Ba计算矩阵的每一列的FFTgydF4y2Ba一个gydF4y2Ba.您可以一次使用MATLAB二进制标量展开函数对矩阵中的每一列执行滤波器的乘法gydF4y2BabsxfungydF4y2Ba.向量化函数是这样的:gydF4y2Ba

函数gydF4y2Bay = fastConvolution_v2(data,filter) m = size(data,1);gydF4y2Ba零垫过滤到数据长度,并转换gydF4y2BaFilter_f = fft(filter,m);gydF4y2Ba转换输入的每一列gydF4y2BaAf = fft(数据);gydF4y2Ba将每一列乘以过滤器并计算逆变换gydF4y2BaY = ift (bsxfun(@times,af,filter_f));gydF4y2Ba结束gydF4y2Ba

使用向量化函数执行相同的实验:gydF4y2Ba

A = complex(randn(4096,100),randn(4096,100));gydF4y2Ba数据输入gydF4y2BaB = randn(16,1);gydF4y2Ba%过滤器输入gydF4y2Bac = fastConvolution_v2(a,b);gydF4y2Ba计算输出gydF4y2Bactime = timeit(@()fastConvolution_v2(a,b));gydF4y2Ba测量CPU时间gydF4y2Badisp ([gydF4y2Ba' CPU上的执行时间= 'gydF4y2Banum2str (ctime)]);ga = gpuArray(a);gydF4y2Ba将数据移动到GPUgydF4y2Bagb = gpuArray(b);gydF4y2Ba移动过滤器到GPUgydF4y2Bagc = fastConvolution_v2(ga, gb);gydF4y2Ba% GPU计算gydF4y2Bagtime = gputimeit(@()fastConvolution_v2(ga,gb));gydF4y2Ba测量GPU时间gydF4y2BaGerr = max(max(abs(gather(gc)-c)));gydF4y2Ba%计算错误gydF4y2Badisp ([gydF4y2Ba' GPU上的执行时间= 'gydF4y2Banum2str (gtime)]);disp ([gydF4y2Ba'最大绝对误差= 'gydF4y2Banum2str (gerr)]);gydF4y2Ba
CPU执行时间= 0.010393 GPU执行时间= 0.0020537最大绝对误差= 1.1374e-14gydF4y2Ba

总之,向量化代码有助于CPU和GPU版本运行得更快。然而,向量化对GPU版本的帮助要比CPU版本大得多。改进后的CPU版本速度几乎是原来的两倍;改进后的GPU比原来的快13倍。GPU代码比原始版本的CPU慢了40%,而修改后的版本快了大约5倍。gydF4y2Ba

故障排除gpugydF4y2Ba

如果你的机器上只有一个GPU,那么你的显卡很可能也扮演着显卡的角色。在这种情况下,您的GPU可能会受到操作系统(OS)超时的影响。你可以这样检查你的GPU:gydF4y2Ba

gpuDevicegydF4y2Ba
ans =gydF4y2Ba
...gydF4y2Ba
KernelExecutionTimeout: 1gydF4y2Ba
如果gydF4y2BaKernelExecutionTimeout = 1gydF4y2Ba,那么你的GPU就会受到操作系统施加的超时,以确保操作系统总是能够将更新打印到屏幕上。如果你的GPU计算花费太多时间,那么该操作将被终止。在这种情况下,必须重新启动MATLAB才能成功恢复GPU计算。gydF4y2Ba

另请参阅gydF4y2Ba

|gydF4y2Ba|gydF4y2Ba|gydF4y2Ba|gydF4y2Ba

这个话题有用吗?gydF4y2Ba