主要内容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脚本gydF4y2Ba测量GPU性能gydF4y2Ba获取有关PCI总线速度、GPU内存读写和双精度矩阵计算的峰值计算性能的信息。gydF4y2Ba

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

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

适用于GPU单精度计算的典型例子包括图像处理和机器学习。gydF4y2Ba//www.tianjin-qmedu.com/content/dam/mathworks/tag-team/Objects/d/Deep_Learning_in_Cloud_Whitepaper.pdfgydF4y2Ba.然而,其他类型的计算,如线性代数问题,通常需要双精度处理。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、mex文件或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上运行MATLAB函数gydF4y2Ba.通常,这些函数支持与在CPU上计算的标准MATLAB函数万博1manbetx相同的参数和数据类型。gydF4y2Ba

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

改进性能的高级工具gydF4y2Ba

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

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

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

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

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

或者,您可以编写一个CUDA内核作为一个mex文件的一部分,并使用mex文件中的CUDA Runtime API调用它。这两种方法都可以让你使用GPU的底层特性,比如共享内存和纹理内存,这些特性在MATLAB代码中是不能直接使用的。有关更多细节,请参见示例gydF4y2Ba使用MEX访问高级CUDA功能gydF4y2Ba.gydF4y2Ba

提高性能的最佳实践gydF4y2Ba

硬件配置gydF4y2Ba

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

在Windows上gydF4y2Ba®gydF4y2Ba在系统中,GPU设备可以处于两种模式之一:Windows Display Driver Model (WDDM)或Tesla Compute Cluster (TCC)模式。为了获得最佳性能,任何用于计算的设备都应该处于TCC模式。更多细节请参阅NVIDIA文档。gydF4y2Ba

NVIDIA的高性能计算设备Tesla line在读写GPU内存时支持纠错码(ECC)。万博1manbetxECC的目的是在读写动态存储器时,纠正正常情况下偶尔发生的位错误。提高性能的一种方法是关闭ECC以增加可实现的内存带宽。虽然可以以这种方式配置硬件,但MathWorks并不推荐这种做法。由于静默错误造成的潜在准确性损失可能比性能收益带来的危害更大。gydF4y2Ba

MATLAB编码实践gydF4y2Ba

本节介绍帮助您在GPU上获得更好性能的通用技术。其中一些技巧也适用于为CPU编写MATLAB代码时。gydF4y2Ba

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

gpu通过并行计算多个结果来实现高性能。因此,矩阵和高维数组操作通常比向量或标量操作的性能要好得多。通过重写循环来使用高维操作,可以获得更好的性能。修正基于循环、面向标量的代码,使用MATLAB进行矩阵和向量运算的过程称为向量化。有关详细信息,请参见gydF4y2Ba使用向量化gydF4y2Ba.gydF4y2Ba

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

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

通过GPU度量性能gydF4y2Ba

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

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

兰德(N =gydF4y2Ba“gpuArray”gydF4y2Ba);fh = @() lu(A);gputimeit (fh, 2);gydF4y2Ba% 2nd arg表示输出数量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] =(一个);等待(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(过滤器,米);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

一个=复杂(randn (4096, 100), randn(4096、100));gydF4y2Ba%数据输入gydF4y2Ba1 b = randn(16日);gydF4y2Ba%滤波器输入gydF4y2Bac = fastConvolution (a, b);gydF4y2Ba%计算输出gydF4y2Bactime时间= (@ ()fastConvolution (a, b));gydF4y2BaCPU时间gydF4y2Badisp ([gydF4y2Ba' CPU的执行时间= 'gydF4y2Banum2str (ctime)]);gydF4y2Ba

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

CPU上的执行时间= 0.019335gydF4y2Ba

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

ga = gpuArray(一个);gydF4y2Ba%移动阵列到GPUgydF4y2Bagb = gpuArray (b);gydF4y2Ba%将滤镜移到GPUgydF4y2Bagc = fastConvolution (ga、gb);gydF4y2Ba在GPU上计算gydF4y2Bagtime = gputimeit (@ () fastConvolution (ga, gb));gydF4y2Ba测量GPU时间gydF4y2Bagerr = max (max (abs(收集(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);gydF4y2Ba%零填充滤波器的长度的数据,并转换gydF4y2Bafilter_f = fft(过滤器,米);gydF4y2Ba%转换输入的每一列gydF4y2Ba房颤= fft(数据);gydF4y2Ba%每列乘以过滤器和计算反变换gydF4y2Bay =传输线(bsxfun (@times,房颤,filter_f));gydF4y2Ba结束gydF4y2Ba

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

一个=复杂(randn (4096, 100), randn(4096、100));gydF4y2Ba%数据输入gydF4y2Ba1 b = randn(16日);gydF4y2Ba%滤波器输入gydF4y2Bac = fastConvolution_v2 (a, b);gydF4y2Ba%计算输出gydF4y2Bactime时间= (@ ()fastConvolution_v2 (a, b));gydF4y2BaCPU时间gydF4y2Badisp ([gydF4y2Ba' CPU的执行时间= 'gydF4y2Banum2str (ctime)]);ga = gpuArray(一个);gydF4y2Ba将数据移动到GPUgydF4y2Bagb = gpuArray (b);gydF4y2Ba%将滤镜移到GPUgydF4y2Bagc = fastConvolution_v2(ga, gb);gydF4y2Ba在GPU上计算gydF4y2Bagtime = gputimeit (@ () fastConvolution_v2 (ga, gb));gydF4y2Ba测量GPU时间gydF4y2Bagerr = max (max (abs(收集(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