矩阵乘法性能优化指南

矩阵乘法是科学计算、工程和机器学习中的基础操作,也是BLAS(基础线性代数子程序)领域的关键例程之一。本文将探讨如何优化矩阵乘法,特别是针对单精度实数矩阵乘法(SGEMM)的性能优化

在Fortran中,SGEMM的API如下:

SGEMM(transa, transb, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc)

该函数执行以下计算:

C := alpha * op(A) * op(B) + beta * op(C)

其中,op(X)可以是X本身或者X的转置(取决于transx参数的值)。数组A和B是输入,而数组C既是输入也是输出。数组A包含一个m×k的矩阵,数组B包含一个k×n的矩阵,数组C包含一个m×n的矩阵。前导维度(lda、ldb和ldc)决定了从一列到下一列的步长,使得GEMM能够在更大的矩阵上工作。前导维度还可以通过使后续列对齐缓存行或映射到8路1级缓存的相同集合来影响性能。

Intel® Math Kernel Library(Intel® MKL)提供了高性能的GEMM实现。优化矩阵乘法的典型方法是将原始输入矩阵的块转换为内部数据格式(如打包格式),通过手工编写的汇编内核乘以转换后的块,然后更新输出矩阵。

选择的块大小旨在最大化缓存和寄存器的使用。打包的原因有很多:

  • 能够将更多的A和B的数据放入缓存中,从而允许更大的块和更多的数据重用。
  • 连续、对齐和可预测的访问,这最小化了缓存和数据转换后备缓冲区(DTLB)的未命中。
  • 减少循环开销。

对于高性能计算中的常规大小,这种基于打包的方法效果很好。一般来说,m和n相对较大,而k可能是中等的(外积)或也相对较大(平方),因此打包输入矩阵所花费的时间相对较短,相对于在计算内核中所花费的时间。然而,对于m或n之一相对较小的大小,如某些机器学习应用中常见的情况,打包开销可能会变得显著。因此,不依赖显式打包的GEMM实现可能会胜过传统的基于打包的GEMM实现。Intel MKL 11.3 Update 3包含了针对Intel® Advanced Vector Extensions 2(Intel® AVX2)和Intel® Advanced Vector Extensions 512(Intel® AVX-512)以及第二代Intel® Xeon Phi™处理器优化的{S,D}GEMM内核。Intel MKL的后续版本继续改进这些内核。

为了看到这些新内核可以带来的好处,图1比较了Intel MKL 2017 Update 1中的SGEMM性能与Intel MKL 11.3 Update 2中可能出现在机器学习中的大小的性能。在这里,m和k分别固定为1000和256,而n变化。性能以GFLOPS(每秒数十亿次浮点运算)给出,因此越高越好。

基于SGEMM调用的特定情况(包括处理器类型和线程计数、问题大小和前导维度以及转置参数),Intel MKL选择使用传统的内核或新的无打包内核。依赖Intel MKL进行SGEMM性能的深度学习框架无需修改框架即可从这些优化中受益。

当一个或多个输入矩阵(A或B)在多个矩阵乘法中重复使用时,可以最小化打包开销;例如,这可能在递归神经网络中出现。在这里,可以一次性支付打包重复使用矩阵的成本,然后在多个SGEMM计算中使用打包版本。Intel MKL 2017引入了{S,D}GEMM的打包API,允许将打包开销分摊到多个矩阵乘法上。对于单精度,四个新的打包API是:

dest = sgemm_alloc (identifier, m, n, k) sgemm_pack (identifier, trans, m, n, k, alpha, src, ld, dest) sgemm_compute (transa, transb, m, n, k, A, lda, B, ldb, beta, C, ldc) sgemm_free (dest)

参数与SGEMM相似,增加了标识符字符,用于标识要打包的矩阵(A或B)。sgemm_compute的transa和transb参数可以像往常一样设置为"T"(转置)或"N"(不转置);此外,值P表示相应的矩阵以内部打包格式存在。如果输入矩阵在多次使用中,打包API将提供好处;因此,sgemm_alloc和sgemm_pack将分别被调用一次以分配内存和打包所需的矩阵为内部打包格式,然后多次调用sgemm_compute,其中打包的矩阵作为输入矩阵之一。最后,调用sgemm_free以释放内存。(有关详细信息,请参见Intel® Math Kernel Library Developer Reference。)

图2显示了sgemm和sgemm_compute(其中A矩阵被打包,并且忽略了打包时间)在GFLOPS中的性能比较。

配置信息 - 版本:Intel® Math Kernel Library(Intel® MKL)2017.1;硬件:Intel® Xeon® Processor E5-2699v4,2个22核CPU(55MB LLC,2.2GHz),64GB RAM;操作系统:RHEL 7.2 GA x86_64。

在性能测试中使用的软件和工作负载可能已经针对Intel微处理器进行了优化。性能测试,如SYSmark和MobileMark,是使用特定的计算机系统、组件、软件、操作和功能进行测量的。任何这些因素的变更都可能导致结果变化。应该咨询其他信息和性能测试,以帮助全面评估正在考虑的购买,包括该产品与其他产品组合时的性能。*其他名称和品牌可能被声明为他人的财产。基准测试来源:Intel Corporation

优化通知:Intel的编译器可能不会针对非Intel微处理器优化到相同的程度,对于不是针对Intel微处理器的独特优化。这些优化包括SSE2、SSE3和SSSE3指令集和其他优化。Intel不保证在非Intel制造的微处理器上任何优化的可用性、功能或有效性。此产品中的微处理器依赖优化旨在与Intel微处理器一起使用。某些不特定于Intel微架构的优化保留给Intel微处理器。有关此通知涵盖的特定指令集的更多信息,请参阅适用的产品用户和参考指南。通知修订#20110804。

图2. 使用Intel® MKL打包API进行更快的矩阵乘法

在GEMM调用之间重复使用较大的A和B输入矩阵的应用程序最有潜力从打包API中受益。如果m和n参数都很大,或者如果两个输入矩阵中较小的一个被重复使用,那么从切换到打包API中获得的性能提升可能不足以证明编程工作是合理的。因此,建议在修改应用程序代码以使用打包API之前,先测量特定用例的性能。

正如所看到的,传统GEMM实现中的内部打包操作可能会有显著的开销,特别是对于一个或多个小尺寸的尺寸。通过使用避免显式打包输入矩阵的内核,或者通过在多个GEMM计算中分摊打包成本,可以减少这种开销。从Intel MKL 11.3 Update 3开始,{S,D}GEMM可以选择使用直接在输入矩阵上操作而不需要首先打包到内部缓冲区的内核;使用的内核是在运行时基于问题特征和处理器信息确定的。或者,Intel MKL 2017引入了打包API,允许显式打包一个或两个输入矩阵,然后在多个矩阵-矩阵计算中重用。这两种方法有助于在多核和众核Intel®架构上实现高性能的GEMM,特别是对于深度神经网络产生的尺寸。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485