案例分享 | TensorFlow XLA 工作原理简介

2020 年 1 月 8 日 TensorFlow
文 /  姜曦楠,腾讯TEG机智团队

腾讯机智机器学习平台是腾讯内部专注机器学习的基础服务平台,其核心服务已成功应用于腾讯多个垂直业务中。机智团队在 TensorFlow 内核开发领域积累了丰富经验。本文将通过一个例子简单介绍 XLA 的工作原理。


XLA 是 TensorFlow 计算图的编译器,只需添加少量代码,即可明显加速 TensorFlow ML 模型。下图是谷歌官方提供的 XLA 性能表现。


下方是所有 XLA 团队基准模型(在 NVidia V100 GPU 上运行)的 TensorFlow 在使用 / 不使用 XLA 时的相对加速 / 减速对比图。

JIT-Just In Time Compilation

我们先来看 XLA 如何作用于 TensorFlow 的计算图,下面是一张简单的 TensorFlow 计算图。


XLA 通过一个 TensorFlow 的图优化 Pass (MarkForCompilation),在 TensorFlow 计算图中找到适合被 JIT 编译的区域。这里我们假设 XLA 仅支持 MatMul 和 Add。


TensorFlow XLA 把这个区域定义为一个 Cluster,作为一个独立的 JIT 编译单元,在 TensorFlow 计算图中通过 Node Attribute 标示。


然后另一个 TensorFlow 的图优化 Pass (EncapsulateSubgraphs),把 cluster 转化成 TensorFlow 的一个 Function 子图。在原图上用一个 Caller 节点表示这个 Function 在原图的位置。


最后调用 TensorFlow 的图优化 Pass (BuildXlaOps),把 Function 节点转化成特殊的 Xla 节点。


在 TensorFlow 运行时,运行到 XlaCompile 时,编译 Xla cluster 子图,然后把编译完的 Executable 可执行文件通过 XlaExecutableClosure 传给 XlaRun 运行。



TF2XLA

TensorFlow 运行到 XlaCompile 节点时,为了编译这个 Function,通过把 TensorFlow 子图所有的节点翻译成 XLA HLO Instruction 虚拟指令的形式表达,整个子图也由此转化成 XLA HLO Computation。



XLA-HLO

XLA 在 HLO 的图表达上进行图优化。聚合可在同一个 GPU Kernel 中执行的 HLO 指令。

HLO 图优化前


HLO 图优化后



代码生成

首先根据虚拟指令分配 GPU Stream 和显存。


然后 IrEmitter 把 HLO Graph 转化成由编译器的中间表达 LLVM IR 表示的 GPU Kernel。LLVM IR 如下所示:

; ModuleID = 'cluster_36__XlaCompiledKernel_true__XlaNumConstantArgs_1__XlaNumResourceArgs_0_.36'
source_filename = "cluster_36__XlaCompiledKernel_true__XlaNumConstantArgs_1__XlaNumResourceArgs_0_.36"
target datalayout = "e-i64:64-i128:128-v16:16-v32:32-n16:32:64"
target triple = "nvptx64-nvidia-cuda"

@0 = private unnamed_addr constant [4 x i8] zeroinitializer
@1 = private unnamed_addr constant [4 x i8] zeroinitializer
@2 = private unnamed_addr constant [4 x i8] zeroinitializer
@3 = private unnamed_addr constant [4 x i8] zeroinitializer
@4 = private unnamed_addr constant [4 x i8] zeroinitializer
@5 = private unnamed_addr constant [4 x i8] zeroinitializer
@6 = private unnamed_addr constant [4 x i8] zeroinitializer

define void @fusion_1(i8* align 16 dereferenceable(3564544) %alloc2, i8* align 64 dereferenceable(3776) %temp_buf) {
entry:
%output.invar_address = alloca i64
%output_y.invar_address = alloca i64
%arg0.1.raw = getelementptr inbounds i8, i8* %alloc2, i64 0
%arg0.1.typed = bitcast i8* %arg0.1.raw to [944 x [944 x float]]*
%fusion.1.raw = getelementptr inbounds i8, i8* %temp_buf, i64 0
%fusion.1.typed = bitcast i8* %fusion.1.raw to [944 x float]*
%0 = call i32 @llvm.nvvm.read.ptx.sreg.tid.x(), !range !4
%thread.id.x = sext i32 %0 to i64
%thread.x = urem i64 %thread.id.x, 944
%thread.y = udiv i64 %thread.id.x, 944
%1 = alloca float
%partial_reduction_result.0 = alloca float
%2 = load float, float* bitcast ([4 x i8]* @0 to float*)
%3 = getelementptr inbounds float, float* %partial_reduction_result.0, i32 0
store float %2, float* %3
%current_output_linear_index_address = alloca i64
%4 = alloca i1
store i1 false, i1* %4
%5 = call i32 @llvm.nvvm.read.ptx.sreg.ctaid.x(), !range !5
%block.id.x = sext i32 %5 to i64
%6 = udiv i64 %block.id.x, 1
%7 = urem i64 %6, 1
%8 = udiv i64 %block.id.x, 1
%9 = urem i64 %8, 8
%10 = udiv i64 %block.id.x, 8
%block_origin.0 = mul i64 %10, 1
%block_origin.1 = mul i64 %9, 1
...


由 LLVM 生成 nvPTX(Nvidia 定义的虚拟底层指令表达形式)表达,进而由 NVCC 生成 CuBin 可执行代码。PTX 如下所示:

//
// Generated by LLVM NVPTX Back-End
//

.version 6.0
.target sm_70
.address_size 64

// .globl fusion_1

.visible .entry fusion_1(
.param .u64 fusion_1_param_0,
.param .u64 fusion_1_param_1
)
.reqntid 944, 1, 1
{
.reg .pred %p<9>;
.reg .f32 %f<25>;
.reg .b32 %r<31>;
.reg .b64 %rd<61>;

ld.param.u64 %rd27, [fusion_1_param_0];
ld.param.u64 %rd28, [fusion_1_param_1];
cvta.to.global.u64 %rd29, %rd28;
cvta.to.global.u64 %rd1, %rd27;
cvta.global.u64 %rd2, %rd29;
mov.u32 %r3, %tid.x;
cvt.u64.u32 %rd3, %r3;
mov.u32 %r1, %ctaid.x;
setp.eq.s32 %p1, %r1, 7;
@%p1 bra LBB0_4;
bra.uni LBB0_1;
LBB0_4:
selp.b64 %rd4, 48, 128, %p1;
cvt.u32.u64 %r26, %rd3;
shl.b64 %rd47, %rd3, 2;
add.s64 %rd48, %rd47, %rd1;
add.s64 %rd59, %rd48, 3383296;
or.b32 %r27, %r26, 845824;
mul.wide.u32 %rd49, %r27, 582368447;
shr.u64 %rd50, %rd49, 39;
cvt.u32.u64 %r28, %rd50;
mul.lo.s32 %r29, %r28, 945;
sub.s32 %r2, %r27, %r29;
mov.f32 %f23, 0f00000000;
...



代码执行

当 TensorFlow 运行到 XlaRun 时运行由 XlaCompile 编译得到的 GPU 可执行代码(Cubin 或 PTX)。


想加入案例分享?点击 “阅读原文”,填写相关信息,我们会尽快与你联系。


更多相关案例:



登录查看更多
8

相关内容

Google发布的第二代深度学习系统TensorFlow
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
【华侨大学】基于混合深度学习算法的疾病预测模型
专知会员服务
96+阅读 · 2020年1月21日
TensorFlow Lite指南实战《TensorFlow Lite A primer》,附48页PPT
专知会员服务
69+阅读 · 2020年1月17日
KGCN:使用TensorFlow进行知识图谱的机器学习
专知会员服务
81+阅读 · 2020年1月13日
【模型泛化教程】标签平滑与Keras, TensorFlow,和深度学习
专知会员服务
20+阅读 · 2019年12月31日
【干货】大数据入门指南:Hadoop、Hive、Spark、 Storm等
专知会员服务
95+阅读 · 2019年12月4日
社区分享 | Spark 玩转 TensorFlow 2.0
TensorFlow
15+阅读 · 2020年3月18日
【教程】TensorFlow2 最新迁移学习教程和实战
TensorFlow 2.0深度强化学习指南
云栖社区
18+阅读 · 2019年2月1日
基于TensorFlow的深度学习实战
七月在线实验室
9+阅读 · 2018年4月25日
TensorFlow神经网络教程
Python程序员
4+阅读 · 2017年12月4日
分布式TensorFlow入门指南
机器学习研究会
4+阅读 · 2017年11月28日
Tensorflow 文本分类-Python深度学习
Python程序员
12+阅读 · 2017年11月22日
教程 | 如何从TensorFlow转入PyTorch
深度学习世界
38+阅读 · 2017年9月30日
【推荐】TensorFlow手把手CNN实践指南
机器学习研究会
5+阅读 · 2017年8月17日
A Modern Introduction to Online Learning
Arxiv
20+阅读 · 2019年12月31日
Real-time Scalable Dense Surfel Mapping
Arxiv
5+阅读 · 2019年9月10日
Arxiv
3+阅读 · 2018年4月9日
VIP会员
相关VIP内容
相关资讯
社区分享 | Spark 玩转 TensorFlow 2.0
TensorFlow
15+阅读 · 2020年3月18日
【教程】TensorFlow2 最新迁移学习教程和实战
TensorFlow 2.0深度强化学习指南
云栖社区
18+阅读 · 2019年2月1日
基于TensorFlow的深度学习实战
七月在线实验室
9+阅读 · 2018年4月25日
TensorFlow神经网络教程
Python程序员
4+阅读 · 2017年12月4日
分布式TensorFlow入门指南
机器学习研究会
4+阅读 · 2017年11月28日
Tensorflow 文本分类-Python深度学习
Python程序员
12+阅读 · 2017年11月22日
教程 | 如何从TensorFlow转入PyTorch
深度学习世界
38+阅读 · 2017年9月30日
【推荐】TensorFlow手把手CNN实践指南
机器学习研究会
5+阅读 · 2017年8月17日
Top
微信扫码咨询专知VIP会员