主要内容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

这是最简单的开始使用MATLAB内置函数的支持gpuArray数据转换你的代码。万博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

如果你有一个纯粹的元素方面的功能,您可以通过调用它提高其性能gydF4y2BaarrayfungydF4y2Ba.这gydF4y2BaarrayfungydF4y2BaGPU上的功能将元素 - WISE MATLAB函数变为自定义CUDA内核,从而减少执行操作的开销。通常,您的应用程序的子集可以使用gydF4y2BaarrayfungydF4y2Ba即使整个应用程序不能。这个例子gydF4y2Ba完善元素方面的MATLAB®功能的性能在GPU上使用ARRAYFUNgydF4y2Ba展示了这种方法的基本概念;和例子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显示驱动程序模型(WDDM)或TESLA计算群集(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和gydF4y2BaTOC.gydF4y2Ba.然而,为了在GPU上获得准确的计时,您必须在调用之前等待操作完成gydF4y2BaTOC.gydF4y2Ba.有两种方法可以做到这一点。您可以拨打gydF4y2Ba收集gydF4y2Ba调用前的最终GPU输出gydF4y2BaTOC.gydF4y2Ba:这迫使所有的计算在时间测量之前完成。或者,你可以用gydF4y2Ba等待gydF4y2Ba与功能gydF4y2BagpuDevicegydF4y2Ba对象作为其输入。例如,如果你想测量计算的时间gydF4y2Ba陆gydF4y2Ba矩阵的分解gydF4y2Ba一个gydF4y2Ba使用gydF4y2Ba抽搐gydF4y2Ba,gydF4y2BaTOC.gydF4y2Ba,gydF4y2Ba等待gydF4y2Ba,你可以这样做:gydF4y2Ba

GD = gpuDevice();TIC();[L,U] = LU(A);等待(GD);TLU = TOC();gydF4y2Ba

你也可以使用MATLAB分析器来显示计算时间是如何在你的GPU代码中分布的。注意,为了完成时间度量,分析器独立地运行每一行代码,因此它不能解释在正常操作期间可能发生的重叠(异步)执行。对于计时整个算法,您应该使用gydF4y2Ba抽搐gydF4y2Ba和gydF4y2BaTOC.gydF4y2Ba,或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测量执行时间gydF4y2BatimeitgydF4y2Ba功能。这gydF4y2BatimeitgydF4y2Ba函数负责常见的基准测试考虑事项,如计算启动和开销。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正在执行FFT,乘法和上长度4096,以增加效果的最佳方式的单独列逆FFT操作是向量化的代码,使得单个MATLAB函数调用执行更多的计算。该FFT和IFFT操作很容易向量化:gydF4y2Bafft (A)gydF4y2Ba计算矩阵每一列的FFTgydF4y2Ba一个gydF4y2Ba.您可以使用MATLAB二进制标量展开函数对矩阵中的每一列执行一次滤波器乘法gydF4y2BabsxfungydF4y2Ba.向量化的功能如下:gydF4y2Ba

函数gydF4y2BaY = fastConvolution_v2(数据,过滤器)M =大小(数据,1);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%计算输出gydF4y2Ba的ctime = timeit(@()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