利用Tensor Core优化GPU性能的几个小窍门

NVIDIA blog
750 0 2019-06-14
经常被问到的问题是“我该怎么做才能让GPU在深度学习方面表现出色?” 

这篇文章里,我们将为每个技巧提供一个通用的指导原则和解释,将指导原则应用到示例层,并比较前后的性能。 

技巧1:激活Tensor Core

Volta和随后的GPU架构上都提供了Tensor Core,它加速了常见的深度学习操作——特别是计算密集型任务,如全连接和卷积层。
工作负载必须使用混合精度来利用张量核。查看我们的帖子里关于自动混合精度快速设置和 混合精度相关的视频培训。此外,当某一层的某些参数可以被8整除(对于FP16数据)或16整除(对于INT8数据)时,Tensor Core就会被激活。一个具有批量大小和数量的输入输出完全连接的层将使用Tensor Core,一个具有相同数量的输入和输出通道的卷积层也将使用Tensor Core。

这是由于gpu存储和访问数据的方式。不满足这个要求的层仍然在GPU上加速。然而,这些层使用32位CUDA核而不是Tensor Core作为后备选项。
注意:在某些情况下,我们放松了需求。然而,遵循这些准则是确保启用Tensor Core的最简单方法。
让我们看两个来自流行的Transformer 神经网络的例子,来说明激活Tensor Core所带来的加速效果。《注意》(Attention Is All You Need) [Vaswani 2017]中描述的Transformer是目前最先进的语言翻译和其他序列任务网络。Transformer网络的大部分由完全连接的层组成。我们将讨论一些Tensor Core的优化方法。

填充词汇表大小-投影层示例

图1显示了Transformer网络的简化表示。网络输出一个向量,其中包含词汇表中每个Token的概率。这个概率向量是使用softmax函数在一个全连接层的输出上生成的,我们将其称为投影层。这一层的输出数量等于词汇表大小,通常超过30,000。考虑到所涉及的重量级计算,确保有效地使用TensorCore是很重要的。

图1所示。投影层(标记为FC)生成一个输出值向量,每个单词在词汇表中输出一个值。Softmax将这些值转换为概率分布;概率最大的单词是此步骤的预测输出。这里用不同的颜色显示了三个步骤。

图2显示了一个这样的投影层的性能,它有1024个输入,批量大小为5120,在特斯拉V100上对FP16数据进行训练。假设我们正在为WMT14任务使用组合的英语-德语培训数据集,其词汇表大小为33708。只需将词汇表大小填充到下一个8的倍数即可激活TensorCore并显著提高吞吐量。

图2,通过选择词汇量大小为8的倍数来激活Tensor Core,可以大大提高投影层的性能。对于显示的所有数据,该层使用1024个输入,批大小为5120。(使用FP16数据,Tesla V100 GPU, cuBLAS 10.1测量)

选择Tensor Core的批大小-前馈层的例子

变压器的结构还包括全连接层,作为self-attention和feed-forward 模块的一部分。让我们考虑前馈块中的第一层,它是一个具有1024个输入和4096个输出的全连接层。该层的批大小取决于批处理程序集,批处理程序集将网络的输入分割为多个批,最大批大小为某个值。当装配不考虑Tensor Core时,可能会产生不规则尺寸的批次。
图3显示了该层的几个批处理大小的训练步骤的性能。这是一个放松Tensor Core要求的例子。无论使用填充还是不使用填充,正向传递和激活梯度传递都执行相同的操作。另一方面,权值梯度传递显示了与图2中相同的显著性能差异。CUDA核被用作权重梯度计算的回退,批量大小为4084或4095令牌,每批使用4088或4096令牌可以实现张量核加速。
 图3。通过选择批量大小为前馈块(1024个输入,4096个输出)中第一个全连接层的8倍,从而激活Tensor Core。相对于CUDA核,Tensor Core的权值梯度通过有明显改善;正演和活化梯度传递证明,Tensor Core在训练的某些部分可以活化,即使参数不能被8整除。(使用FP16数据,Tesla V100 GPU, cuBLAS 10.1测量)

当任何相关参数都不是最佳大小时,至少有一个正向、激活梯度和权值梯度通道不会被张量核加速。我们建议确保所有这些参数在使用FP16进行训练时都是8的倍数,在使用INT8进行训练时都是16的倍数。这些包括批量大小和输入和输出的数量,对于全连接层和通道的进出,对于卷积层。这是保证Tensor Core将加速您的任务的最简单的方法!

检查TensorCore的使用情况

您可以使用英伟达的分析工具来检查Tensor Core是否被激活。有关这些工具的更多信息可以在CUDA文档中找到。
注:虽然我们在这篇文章中关注的是Tensor Core,但没有张量核加速的深度学习操作也对整体网络性能有贡献。

技巧2:考虑量化效果

到目前为止,我们主要关注的是如何确保Tensor Core加速你的任务。现在让我们讨论GPU的效率和一些参数调整,可以帮助你得到最大限度的Tensor Core。
gpu同时执行许多计算;我们将这些并行计算称为线程。从概念上讲,线程被分组为线程块,每个线程块负责执行计算的一个子集。当GPU执行任务时,它被分割成大小相同的线程块。
现在考虑一个完全连接的层。在训练过程中,正向传播、激活梯度计算、权值梯度计算均用矩阵乘法表示。GPU将输出矩阵划分为大小均匀的矩形tiles。每个tiles由一个线程块计算;图4演示了一个这样的tile的过程。您可以找到多个线程块构成一个tile的情况,但是为了简单起见,我们将在本文中假设每个tile有一个线程块。 

图4,对于大小分别为M * K和K * N的两个矩阵相乘,是分别在前者中取大小为Mtile * Ktile,后者中取大小为Ktile * Ntile的不同滑块,(进行局部乘加)得到C中大小为Mtile * Ntile的滑块,然后两个矩阵分别在各自的横向和纵向进行滑动,完成得到整体C的计算。

然而,并不是所有的输出矩阵都能均匀地分割成可用的平铺大小。此外,创建的线程块可能不会在GPU上的多处理器之间平均分配。这些效应分别称为平铺量化和波量化,会导致周期浪费和效率低下。

拆分到最接近的分块(块量化)发生在输出矩阵大小无法被分块大小整除的时候,使用像其他前面的那些块一样,执行同样的计算量,来执行处理最边沿行列的线程块,但运算结果中只有一部分是有用的。 如果你用cublas库的话,它会自动尝试使用最佳的分块大小的,大部分块的大小都是2的整数次方。为了避免拆分到最接近的块(块量化),选择最佳的矩阵大小,能被2的幂整除的那种(最少建议选择64,理想选择256, 这对用上大多数的分块形状/大小来说,都比较合适。)

 我们还考虑了可以在GPU上并行运行的线程块的数量,以便拆分到最接近的块。以Tesla V100 GPU为例,它有80个SM,tile大小为256×128,其中V100 GPU可以在每个SM上执行一个线程块。在这种情况下,一波80个线程块完全占据GPU。假设一个任务创建96个线程块。前80个线程块将被有效地计算为“full wave”,而剩下的16个线程块将组成一个低效的“tail wave”,在此期间GPU没有得到充分利用。图5演示了这种情况的一个简单版本。

 图5。在8个SM的GPU上,一个12个线程块的任务将被分成8个线程块(占据所有SM)和4个线程块(只占SM的50%)的尾波。因此,该任务的GPU总利用率最多为75%。

(注意:尾波就是最边界的不能整除的不完整的块)

如果没有关于将使用什么块大小的信息,请选择参数,使块/线程块的总数可以被多处理器的数量整除,以避免波动量化效果。

现在让我们看看如何将其映射回一个全连接层的参数。图6显示了用于正向、激活梯度和权重梯度传递的等效矩阵乘法的维数。



图6。对于(a)正向传播,(b)激活梯度计算,(c)全连通层的权值梯度计算,等价矩阵相乘。

批次大小在正向和激活梯度传递过程中直接控制输出矩阵的宽度。再次考虑我们前面的Transformer前馈块(具有1024个输入和4096个输出的全连接层)中的第一层示例。在正向传播过程中,输出矩阵的形状为4096 x批大小。假设tiles大小为256×128,这个矩阵分为4096/256 = 16行和(批量大小)/ 128列tiles

避免平铺量化很简单:批大小应该能被128整除。波量子化更为复杂。对于某个整数n,我们想要n*80个tiles并且已经知道总共有16行tiles。因此,我们的任务应该创建n*5列tile。给定平铺宽度为128,这对应于输出矩阵宽度(和批大小)为n*5*128 = n*640。因此,选择批量大小可被640整除避免了波动量化效应。

选择量化用的batch size——前馈层的例子

图7显示了我们的示例前馈层在几种不同批大小下的性能。选择无量子化的批大小(2560而不是2048,5120而不是4096)可以显著提高性能。注意,批处理大小为2560(导致80个线程块的4个波动)比批处理大小为4096(总共512块,导致80个线程块的6个波动和剩余32个线程块的尾波)的吞吐量更高。权重梯度传递没有显示这种剧烈的变化。批量大小映射到矩阵的“K”维数在此遍历期间相乘,因此不能直接控制输出矩阵的大小或创建的tile和线程块的数量。


图7。选择批量大小以避免波动量化效应,提高了前向块(1024输入,4096输出)中第一全连接层在前向和激活梯度通道中的性能。对于重量梯度传递,波量子化不会在批处理大小上发生。(使用FP16数据,Tesla V100 GPU, cuBLAS 10.1测试)