TVM优化GPU机器翻译

背景

神经机器翻译(NMT)是一种自动化的端到端方法,具有克服传统基于短语的翻译系统中的弱点的潜力。最近,阿里巴巴集团正在为全球电子商务部署NMT服务。

将Transformer用作NMT系统的关键技术,相对于基于经典RNN / LSTM的模型具有同等(甚至更高)的精度,对于高效的离线训练更为友好。尽管Transformer在离线训练阶段很友好,打破了跨时间步长的依赖性,但在线推理效率不高。在生产环境中,已经发现,初始版本的Transformer的推理速度约为1.52倍,比LSTM版本慢。已经进行了一些优化来提高推理性能,例如图形级op融合,循环不变节点运动。观察到的一个特殊挑战是,批处理matmul是Transformer中的主要性能热点,而cuBLAS中的当前实现并未得到很好的优化。

下面的结果表明TVM生成内核(与调度表优化)带来至少13X批量MATMUL计算加速,futher加快算子融合功能。

批处理Matmul

为什么批量Matmul

在Transformer中,批处理matmul广泛用于multi-head attention计算。使用matmul批处理,关注层中multiple heads并行运行,帮助提高硬件的计算效率。

在推理阶段对Transformer模型进行了全面的分析,结果表明,批量matmul计算贡献了约30%的GPU内核执行时间。使用nvprof,对cuBLAS批处理matmul内核进行一些第一性原理分析,可以清楚地表明,当前的实现方式表现不佳,并且观察到了一些有趣的现象。

什么是批量matmul

通常,批量矩阵计算对一批矩阵执行乘法。批处理被认为是“统一的”,即所有实例具有相同的维度(M,N,K),前导维度(lda,ldb,ldc),以及各自的A,B和C矩阵的转置。

批量matmul计算,可以更具体地描述如下:

  1. void BatchedGemm(input A, input B, output C, M, N, K, batch_dimension) {
  1.   for (int i = 0; i < batch_dimension; ++i)  {
  1.     DoGemm(A[i],B[i],C[i],M,K,N)
  1.   }
  1. }

批处理matmul形状

在语言翻译任务中,在其它工作负载中,批处理matmul的形状显着小于常规matmul计算。Transformer中的形状与输入语句的长度和解码器步长有关。通常小于30。

对于批处理尺寸,给定一定的推断批处理大小,这是一个固定的数字。例如,如果将16用作光束大小为4的批大小,则批大小为16 * 4 * #head(多头注意中的头数,通常为8)。矩阵M,K,N的形状,在[1,最大解码长度]或[1,最大编码长度]的范围内。

cuBLAS batch matmul的性能问题

首先,对批处理matmul内核进行了理论上的FLOP分析。结果非常有趣:所有批处理Matmul的计算强度都有限(少于1个TFLOP)。

然后,通过nvprof剖析了具有多种形状的matmul批处理cuBLAS性能。下表显示了在带有CUDA8.0的NVIDIA M40 GPU上获得的一些指标。

输入形状

[
批,MNK]

核心

理论FLOP

nvprof观察到的FLOP

理论FLOPs /

观察到的FLOPs

[512、17、17、128]

maxwell_sgemmBatched_128x128_raggedMn_tn

18939904

2155872256

0.87%

[512、1、17、128]

maxwell_sgemmBatched_128x128_raggedMn_tn

1114112

2155872256

0.052%

[512,17,1,128]

maxwell_sgemmBatched_128x128_raggedMn_tn

1114112

2155872256

0.052%

[512,30,30,128]

maxwell_sgemmBatched_128x128_raggedMn_tn

58982400

2155872256

2.74%

即使具有不同的形状(M,N,K不同),所有maxwell_sgemmBatched_128x128_raggedMn_tn调用,也执行相同数量的FLOP,这比理论值大得多。可以推断出,所有这些不同的形状都可以被填充成一定的形状。在所有这些形状中,即使在最佳情况下,理论上的FLOP仍然仅是实际执行的FLOP的2.74%,因此,大多数计算都是相当多余的。同样,另一个cuBLAS内核maxwell_sgemmBatched_64x64_raggedMn_tn的调用显示相同的现象。

显然,cuBLAS的批量matmul实施远非效率。因此,使用TVMNMT工作负载生成有效的批处理matmul内核。

批量matmul计算

在TVM中,一般的批量Matmul计算可以声明为:

  1. # computation representation
  1. A = tvm.placeholder((batch, M, K), name='A')
  1. B = tvm.placeholder((batch, K, N), name='B')
  1. k = tvm.reduce_axis((0, K), 'k')
  1. C = tvm.compute((batch, M, N),
  1.          lambda b, y, x: tvm.sum(A[b, y, k] * B[b, k, x], axis = k),
  1.          name = 'C')

调度优化

在宣布计算之后,需要精心设计自己的调度表以压缩性能潜力。

块/线程数的调整参数

  1.   # thread indices
  1.   block_y = tvm.thread_axis("blockIdx.y")
  1.   block_x = tvm.thread_axis("blockIdx.x")
  1.   thread_y = tvm.thread_axis((0, num_thread_y), "threadIdx.y")
  1.   thread_x = tvm.thread_axis((0, num_thread_x), "threadIdx.x")
  1.   thread_yz = tvm.thread_axis((0, vthread_y), "vthread", name="vy")
  1.   thread_xz = tvm.thread_axis((0, vthread_x), "vthread", name="vx")
  1.  
  1.   # block partitioning
  1.   BB, FF, MM, PP = s[C].op.axis
  1.   BBFF = s[C].fuse(BB, FF)
  1.   MMPP = s[C].fuse(MM, PP)
  1.   by, ty_block = s[C].split(BBFF, factor = num_thread_y * vthread_y)
  1.   bx, tx_block = s[C].split(MMPP, factor = num_thread_x * vthread_x)
  1.   s[C].bind(by, block_y)
  1.   s[C].bind(bx, block_x)
  1.   vty, ty = s[C].split(ty_block, nparts = vthread_y)
  1.   vtx, tx = s[C].split(tx_block, nparts = vthread_x)
  1.   s[C].reorder(by, bx, vty, vtx, ty, tx)
  1.   s[C].reorder(by, bx, ty, tx)
  1.   s[C].bind(ty, thread_y)
  1.   s[C].bind(tx, thread_x)
  1.   s[C].bind(vty, thread_yz)
  1.   s[C].bind(vtx, thread_xz)

融合了批处理matmul的外部尺寸,即op尺寸的BB和FF,在批处理matmul计算中通常称为“批处理”尺寸。然后将外部和内部尺寸除以(number_thread * vthread)。

在批处理matmul中不需要交错模式,因此虚拟线程号(vthread_yvthread_x)都设置为1。

寻找number_thread的最佳组合

以下结果是在具有CUDA8.0的NVIDIA M40 GPU设备上获得的。

输入形状[批处理,特征,MNK]

num_thread_ynum_thread_x

num_vthread_ynum_vthread_x

时间(us

[64,8,1,17,128]

8,1

32,1

37.62

[64,8,1,17,128]

4,1

32,1

39.30

[64,8,1,17,128]

1,1

32,1

38.82

[64,8,1,17,128]

1,1

256,1

41.95

[64,8,1,17,128]

32,1

1,1

94.61

以往的经验了解到,该方法找到的最佳组合num_thread_ynum_thread_x通过强力搜索。经过蛮力搜索后,可以找到当前形状的最佳组合,在当前计算中为num_thread_y= 8和num_thread_x= 32。

将批处理matmul 与其它算子融合

通常,现有的“黑盒” cuBLAS库,调用扮演着通常使用的“ op Fusion”优化策略的边界的角色。但是,利用所生成的高效批处理matmul核,可以容易地打破融合边界,不仅可以融合元素的操作方式,还可以进一步提高性能。

从计算图可以看出,批处理matmul总是跟在添加操作或转置操作广播之后。通过将“ add”或“ transpose”操作与批处理matmul融合,可以减少内核启动开销和冗余内存访问时间。

批处理matmul和添加融合广播计算,可以声明如下:

  1. # computation representation
  1. A = tvm.placeholder((batch_size, features, M, K), name='A')
  1. # the shape of B is (N, K) other than (K, N) is because B is transposed is this fusion pattern
  1. B = tvm.placeholder((batch_size, features, N, K), name='B')
  1. ENTER = tvm.placeholder((batch_size, 1, M, N), name = 'ENTER')
  1. k = tvm.reduce_axis((0, K), 'k')
  1. C = tvm.compute(
  1.            (batch_size, features, M, N),
  1.            lambda yb, yf, m, x: tvm.sum(A[yb, yf, m, k] * B[yb, yf, x, k], axis = k),
  1.            name = 'C')
  1. D = topi.broadcast_add(C, ENTER)

批处理matmul和转置融合计算可以声明为:

  1. # computation representation
  1. A = tvm.placeholder((batch_size, features, M, K), name='A')
  1. B = tvm.placeholder((batch_size, features, K, N), name='B')
  1. k = tvm.reduce_axis((0, K), 'k')
  1. C = tvm.compute(
  1.            (batch_size, M, features, N),
  1.            lambda yb, m, yf, x: tvm.sum(A[yb, yf, m, k] * B[yb, yf, k, x], axis = k),
  1.            name = 'C')

融合内核性能

选择[batch = 64,heads = 8,M = 1,N = 17,K = 128]的形状,详细说明所生成代码的性能。选择17作为序列长度,这是生产场景中的平均输入长度。

  • TF-R1.4 BatchMatmul:513.9
  • TF-R1.4 BatchMatmulTranspose(另购):541.9
  • TVM BatchMatmul:37.62美元
  • TVM BatchMatmulTranspose(融合):38.39美元

内核融合优化进一步提高了1.7倍的速度。

与Tensorflow集成

批量matmul在工作量中的输入形状是有限的,可以很容易地预先枚举。使用这些预定义的形状,可以提前生成高度优化的CUDA内核(固定形状计算可以带来最佳的优化潜力)。将生成适用于大多数形状的通用批处理matmul内核,为没有相应的提前生成的内核的形状提供回退机制。

针对特定形状生成的高效内核和后备内核已集成到Tensorflow框架中。开发了融合操作,例如BatchMatMulTranspose或BatchMatMulAdd,使用TVM的runtime API生成特定生成的内核,实现某些输入形状或调用后备内核。进行图形优化遍历,用算子融合自动替换原始批处理matmul +添加/转置模式。通过结合更积极的图形优化过程,试图利用TVM为长尾算子模式生成更有效的融合内核,以进一步提高端到端性能。

概括

在阿里巴巴内部,发现TVM是开发高性能GPU内核,满足内部需求的非常有效的工具。以NMT Transformer模型为例来说明使用TVM的优化策略。首先,通过第一性原理分析确定了Transformer模型的热点。然后使用TVM生成高度优化的CUDA内核来取代CUBLAS版本(13X加速观察)。接下来,利用TVM的内核融合机制融合批处理matmul的先前/以下操作,以进一步提高性能(进一步提高1.7倍的性能)。端到端性能提高了1.4。基于这些生成的内核,开发了图优化遍历以自动用TVM融合内核替换原始计算模式,确保优化对最终用户是透明的,作为AI基础设施提供商,发现优化策略的透明性对于推广其优化算法非常重要采用。最后,并非最不重要的一点是,所有这些优化都以松散耦合的方式集成到TensorFlow中,展示了将TVM与不同的深度学习框架集成的潜在方法。此外,正在进行一项将TVM集成为TensorFlow的代码源后端的工作,希望将来能与社区共享更多结果。

TVM优化GPU机器翻译的更多相关文章

  1. TensorFlow+TVM优化NMT神经机器翻译

    TensorFlow+TVM优化NMT神经机器翻译 背景 神经机器翻译(NMT)是一种自动化的端到端方法,具有克服传统基于短语的翻译系统中的弱点的潜力.本文为全球电子商务部署NMT服务. 目前,将Tr ...

  2. TVM 优化 ARM GPU 上的移动深度学习

    TVM 优化 ARM GPU 上的移动深度学习 随着深度学习的巨大成功,将深度神经网络部署到移动设备的需求正在迅速增长.与桌面平台上所做的类似,在移动设备中使用 GPU 既有利于推理速度,也有利于能源 ...

  3. TVM优化Deep Learning GPU算子

    TVM优化Deep Learning GPU算子 高效的深度学习算子是深度学习系统的核心.通常,这些算子很难优化,需要HPC专家付出巨大的努力. 端到端张量IR / DSL堆栈TVM使这一过程变得更加 ...

  4. unity优化-GPU(网上整理)

    优化-GPUGPU与CPU不同,所以侧重点自然也不一样.GPU的瓶颈主要存在在如下的方面: 填充率,可以简单的理解为图形处理单元每秒渲染的像素数量.像素的复杂度,比如动态阴影,光照,复杂的shader ...

  5. GPU 编程入门到精通(五)之 GPU 程序优化进阶

    博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识.鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程. 有志同道合的小伙 ...

  6. GPU 编程入门到精通(四)之 GPU 程序优化

    博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程.有志同道合的小伙伴 ...

  7. TVM:一个端到端的用于开发深度学习负载以适应多种硬件平台的IR栈

    TVM:一个端到端的用于开发深度学习负载以适应多种硬件平台的IR栈  本文对TVM的论文进行了翻译整理 深度学习如今无处不在且必不可少.这次创新部分得益于可扩展的深度学习系统,比如 TensorFlo ...

  8. TVM如何训练TinyML

    TVM如何训练TinyML 机器学习研究人员和从业人员对"裸机"(低功耗,通常没有操作系统)设备产生了广泛的兴趣.尽管专家已经有可能在某些裸机设备上运行某些模型,但是为各种设备优化 ...

  9. 桥接PyTorch和TVM

    桥接PyTorch和TVM 人工智能最引人入胜的一些应用是自然语言处理.像BERT或GPT-2之类的模型及其变体,可以获住足够多的文本信息. 这些模型属于称为Transformers的神经网络类体系结 ...

随机推荐

  1. 基于Docker安装的MindSpore-1.2 GPU版本

    技术背景 在前面一篇博客中,我们介绍过MindSpore-CPU版本的Docker部署以及简单的案例测试,当时官方还不支持GPU版本的Docker容器化部署.经过MindSpore团队的努力,1.2. ...

  2. linux下python调用.so文件

    前言 使用python 调用Fanuc的动态链路库.so 文件读取数据 环境要求 环境 需求 ubuntu16.04 32位 python3.5 32位 配置 把so文件添加到默认路径 ln -s / ...

  3. Appium命令行启动,提示找不到命令,本地没有appium.cmd文件

    安装appium时,直接从github上下载的appium-desktop-windows版本,安装后,从打开桌面端Server,能启动服务,appium-doctor也能正常运行. 但奇怪的地方来了 ...

  4. hdu3594 强连通 tarjan

    题意: 判断是不是强连通图 ,同时每一条边必须只能在一个环里 思路:之前我的强连通用的全是双深搜,结果题目的第二个要求很难判断,一开始写了三个深搜加上并查集,结果越写越乱,其实就是在判断一个边是否只在 ...

  5. hdu4912 LCA+贪心

    题意:       给你一棵树和m条边,问你在这些边里面最多能够挑出多少条边,使得这些边之间不能相互交叉. 思路:      lca+贪心,首先对于给的每个条边,我们用lca求出他们的公共节点,然后在 ...

  6. hdu5248序列变换(二分+贪心)基础题

    题意(中文的直接粘题意吧)                                                                                      序 ...

  7. dispatcherServlet-servlet.xml(SSM maven 项目)

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  8. python模块一之faker模块

    faker模块主要是用来创建伪数据,无需手动生成或者手写随机数来生成数据,可以利用faker完成伪造大量测试数据的工作 一.安装 pip install faker 二.使用 from faker i ...

  9. oo第四单元作业总结

    一.本单元两次作业的架构: 本单元两次作业的架构基本是一致的,所以两次作业的架构就一起说了. 为了避免查询时出现同一个结果反复计算的情况(连续两次查询一个类的顶级父类,如果我们在查询的指令中来计算其父 ...

  10. Jmeter(一) - 从入门到精通 - 环境搭建(详解教程)

    1.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静态和动态资源的性能,例如:静态文件, ...