TensorFlow 拆包(七):Profiling 踩坑

接上篇:

开始分析性能瓶颈了,本篇记录一下研究 TF 中自带的 Profiling 工具时遇到的几个坑点。

profiler

大概 17 年 5 月左右,/tensorflow/core/ 中新加了一个 profiler 的目录,里面是把原本在 contrib 中的 profiling 工具移过来了,大概正式 release 应该是在 1.6、1.7 里面。

关于生成 profiling 的 context 文件详见 tf.profiler 相关的内容,这里直接开始记录怎么用 tfprof 这个工具。

试了一下 pip 包里面应该是没有单独包含的,需要从源码手动编译:

1
bazel build //tensorflow/core/profiler

然后使用也是要从 bazel-bin 的目录中打开:

1
bazel-bin/tensorflow/core/profiler/profiler --profile_path=xxxxx

profiler_ui

profiler 的 README 中,示例代码除了 profiler 以外还有个 profiler_ui,基本上是一个类似 tensorboard 的网页前端,方便调用后端的 profiler 进行可视化查看用的。

这里虽然写着暂未开源,但是在 TensorFlow 的 github 总目录里面可以找到一个叫 profiler-ui 的项目,就是那个未完善开源的 ui 版了。

看了下,安装需要用到 go 以及 Google 自家的 pprof 工具,可能是因为耦合的其他部件比较多,所以暂时还没有并入 TF 的主代码中去。不过这里的 Installation 已经足够我们自己装上了。

装 pprof 的时候会有个坑点,CentOS 库中可以找到 gperftools 这个工具,也是 Google 提供的,yum 装上之后可执行文件的名字也叫 pprof !!但是跟这里用到的 pprof 不是一个玩意!!

之后按照示例上的说明:

1
python ui.py --profile_context_path=xxxx

即可启用。

在我尝试使用它的时候,距离这个库上一次 git 的更新已经过去 1 个月左右了,不知道是 python 版本还是什么原因,直接运行可能会遇到找不到 server 的路径等等的 bug,直接在 ui.py 里面稍微改一下就好。

Profiling

运行 TF 时保存出来的 profiling 文件包含了大量信息,主要有几个方面:

  • scope:应该是 python 层代码中用 tf.name_scope() 包起来的视图
  • graph:TensorFlow 计算图的视图
  • op:把 TensorFlow 计算图再细化一层
  • code:Python 代码视图

默认会按列表把所选的视图中的一些信息给输出出来,另外用-output 选项可以指定输出成另外的格式:

1
2
3
4
5
6
7
8
9
10
tfprof>
xxx xxx -output timeline:outfile=xxxxx
# 把结果输出成 chrome 用的时间线 trace 文件,可以在 chrome 地址栏中输入 chrome://tracing 打开
# 只支持 graph、scope、code 这 3 种视图
xxx xxx -output pprof:outfile=xxxxx
# 把结果输出成 pprof 用的可视化文件(所以前面装 pprof 就是为了这个)
# 只支持 code 这种视图
# --------------------------------------------------------------------------
# pprof 可视化文件之后可以用 pprof 来变成图片(猜测大概是类似 GraphViz 的数据结构)
pprof -svg --nodecount=10000 --sample_index=1 xxxxxx.prof > xxxxxx.svg

profiler_ui 打开时的第一个页面就是 graph 视图生成的 timeline:

其中包含了计算图中每个 node 在卡上的情况,运行时间、数据流动依赖关系等等。(话说显示的太复杂了,事实上我觉得还是很难看)

然后默认的 scope 视图以及 code 视图得到的 timeline 我也感觉并没有什么用。

code 视图输出成的 pprof 图片倒是还可以看一下,但是感觉用处也不大

所以最后感觉还是不知道该怎么用好这套 profiling 工具

Options

在 tfprof 界面直接回车可以看到默认的选项,然后这里面的内容都是可以改的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
tfprof>
-max_depth 10
-min_bytes 0
-min_peak_bytes 0
-min_residual_bytes 0
-min_output_bytes 0
-min_micros 0
-min_accelerator_micros 0
-min_cpu_micros 0
-min_params 0
-min_float_ops 0
-min_occurrence 0
-step -1
-order_by name
-account_type_regexes .*
-start_name_regexes .*
-trim_name_regexes
-show_name_regexes .*
-hide_name_regexes
-account_displayed_op_only false
-select micros
-output stdout:

稍微挑几个写一下:

-max_depth:指定显示前多少个 node(配合下面的 -order_by ?)

-step:profiling 记录的文件可能包含了很多个 step,用这个选项来指定当前分析哪个 step 的信息,默认 -1 是对所有 step 做平均

-order_by:打出来列表的时候,按照什么来排序:

  • name:node 的名称
  • depth:node 在节点树中的深度
  • bytes:占用的内存数
  • peak_bytes:占用的峰值内存数
  • residual_bytes:计算完成之后,还剩下不释放的内存数
  • output_bytes:输出的大小
  • micros:node 计算所花费的时间
  • accelerator_micros:node 计算所花费的加速卡时间(区别于 CPU 的其他设备)
  • cpu_micros:node 计算所花费的 CPU 时间
  • params:node 中包含的参数量
  • float_ops:node 所需要的浮点运算次数
  • occurrence:node 在图中出现的次数

-account_type_regexes:筛选出类型里面带有某些前缀的 node 有多少个

-start_name_regexes:筛选出名字中带某些前缀的 node

-trim_name_regexes:隐藏掉名字中带某些前缀的 node

-show_name_regexes:筛选出名字中带某些字符的 node

-hide_name_regexes:隐藏掉名字中带某些字符的 node

-select:选择视图中的哪些内容(有点像从数据库里面找东西的感觉),输出 timeline 的时候配合这个应该能够得到不同的数据:

  • bytes:占用的内存数
  • peak_bytes:占用的峰值内存数
  • residual_bytes:计算完成之后,还剩下不释放的内存数
  • output_bytes:输出的大小
  • micros:计算所花费的时间
  • accelerator_micros:计算所花费的加速卡时间
  • cpu_micros:计算所花费的 CPU 时间
  • params: 参数量
  • float_ops:浮点运算次数
  • occurrence:在计算图中出现的次数
  • tensor_value:tensor 数据的值(估计需要配合 checkpoint 用)
  • device:op 放在哪个设备上
  • op_types:op 类型
  • input_shapes:输入的形状
0%