BMProfile 使用

BMProfile是将用户产生的profile信息以可视化的方式展示出来。 主要作用是辅助用户进行网络的性能分析,查看Graph、SubGraph、layer、算子、指令等各个级别的性能数据和Memory使用情况。 此外,还可以对静态网络的指令进行分析过滤,以分析出越界读写等问题。

BM1684 Profile

1. profile安装

请用pip3安装SDK内的bmprofile安装包

pip3 install bmprofile-x.x.x-py2.py3-none-any.whl

2. profile使用方法

对编译网络进行profile

  1. 在编译时,开启enable_profile选项(可选,如果没有加,则profile显示时没有相关层的关联信息)

  • bmnetx命令行参数加上 --enable_profile=True

  • bmlang在调用编译接口时,令 enable_profile=1

  1. 在实际设备上运行bmodel(如bmrt_test、调用bmruntime运行接口)前,设置环境变量

  • export BMRUNTIME_ENABLE_PROFILE=1

  1. 运行runtime程序, 运行结束后在运行目录下生成bmprofile_data-N文件夹,里面包含二进制的profile数据。其中N为设备id号,如设备id为1,则输出目录为bmprofile_data-1

  2. 利用bmprofile工具可以实现以下两种功能

  • 运行命令 python -m bmprofile  [--mode time]  bmprofile_data [out_dir] 生成out_dir,然后用浏览器打开out_dir里的result.html观察时间结果 将bmprofile数据的图形化显示。 其中out_dir是可选的,默认值为bmprofile_out,该文件夹内包含三个文件:result.html, echarts.min.js, profile_data.js。 如果浏览器没有自动打开,请自行用浏览器打开out_dir里的result.html观察结果 (使用说明见”数据显示说明”章节)

  • 运行 python -m bmprofile  --mode command bmprofile_data-N out_dir [--test 'condition'], 解析静态指令,生成可读文本命令,用于检查定位指令问题。 用文本工具打开out_dir里的cmd_xxx_y.log查看静态指令配置(其中xxx表示ddr的加载地址,y=1表示是gdma指令, y=0表示是bd指令) (使用说明见”静态指令解析说明”章节)

  1. 如不再使用profile功能,请清除环境变量,以免影响运行速度

unset BMRUNTIME_ENABLE_PROFILE

注意事项:
使能profile运行bmruntime有可能发生hang住情况,目前原因还在排查,处理方法:
  • 通过设置环境变量BMRUNTIME_DISABLE_GDMA_PERF=1、BMRUNTIME_DISABLE_BDC_PERF=1、 BMRUNTIME_DISABLE_ARM_PERF=1分别来禁用掉gdma,bdc,arm上的profile功能 可组合使用,有可能禁用bdc后可以正常运行或禁用gdma后可以正常运行,需要尝试。

  • 如果hang住,需要断电重启重新安装驱动后,重新尝试

对BMKERNEL进行profile

  1. 在bmkernel的host程序运行前设置环境变量: export BMLIB_ENABLE_ALL_PROFILE=1

  2. 运行bmkernel的host程序,在当前目录生成bmprofile_data-N文件夹

  3. 运行命令 python -m bmprofile  [--mode time]  bmprofile_data-N [out_dir]

    生成out_dir,然后用浏览器打开out_dir里的result.html观察时间结果 将bmprofile数据的图形化显示。 其中out_dir是可选的,默认值为bmprofile_out,该文件夹内包含三个文件:result.html, echarts.min.js, profile_data.js。 如果浏览器没有自动打开,请自行用浏览器打开out_dir里的result.html观察结果 (使用说明见”数据显示说明”章节)

  4. 如不再使用profile功能,请清除环境变量,以免影响运行速度

unset BMLIB_ENABLE_ALL_PROFILE

注意事项:
使能profile运行bmkernel host程序有可能发生hang住情况,目前原因还在排查,处理方法:
  • 通过设置环境变量BMLIB_ARM_RECORD_SIZE=0、BMLIB_BDC_RECORD_SIZE=0、BMLIB_GDMA_RECORD_SIZE=0分别将对应大小设置为0, 来分别禁用掉arm, bdc, gdma上的profile功能. 如需要恢复默认大小请用unset BMLIB_ARM_RECORD_SIZE,unset BMLIB_BDC_RECORD_SIZE,unset BMLIB_GDMA_RECORD_SIZE。

  • 如果hang住,需要断电重启重新安装驱动后,重新尝试

  • 目前发现firmware上有可能由于dtcm内存耗尽(用malloc申请内存),导致运行卡住,可以设置环境变量BMLIB_ARM_RECORD_SIZE=0禁用arm的profile来减少内存占用,但profile的bdc和gdma的起始时间无法确定

3. 数据显示说明

../_images/profile_seq.png

序列图标号功能说明:

  • 1-选择放大功能

  • 2-恢复上一选择放大视图

  • 3-重置显示

  • 4-下载当前显示图片

  • 5-SUBNET:按迭代来显示子网时间占用及信息

  • 6-Host_CPU:显示CPU layer,Merge,Switch占用时间信息

  • 7-LAYER:包括静态和动态TPU layer信息,其中以半高显示的为group中的GDMA的操作(因为有可能和local layer显示时间重叠),其它的是global layer和local layer

  • 8-NODE_OP:显示在arm上设置原子操作(set_GDMA, set_BD)及poll_all_engine_done(wait)的时间信息,其中设置原子操作只是一个时点信息,宽度没有意义; poll_all_engine_done是时长信息宽度为等待时间

  • 9-TPU_GDMA:是gdma各个操作实际占用的时间,宽度表示占用时长,高度表示瞬时带宽,越高表示带宽利用率越高

  • 10-TPU_BDC:表示实际计算时间

  • 11-用于过滤数据,可以根据layer_id,layer_type, subnet_id, subnet_type, iteration过滤部分数据,序列图左侧统计数会根据该过滤条件变化

  • 12-切换显示标号15所示的数据表

  • 13-切换显示标号LOCAL MEM图表,适用于静态指令,由于显示比较耗时,故默认为不显示,具体操作说明见下方

  • 14-切换显示标号GLOBAL MEM图表,适用于静态指令,由于显示比较耗时,故默认为不显示,具体操作说明见下方

  • 15-数据表。可切换显示上面序列图中SUBNET,HOST_CPU,LAYER,NODE_OP,TPU_GDMA,TPU_BDC数据。最多显示前100条,点击表头可按对应列排序,点击数据行可以在时序图中快速定位该数据位置。最后一列表示实际占用时间

内存图显示说明

../_images/profile_lmem.png ../_images/profile_gmem.png
  • 内存图上方曲线为读写曲线,红色表示写,绿色表示读,黑色表示读写总量。含义是当前时点,读写发生的总内存大小,用于查找内存操作的时间区域

  • 左侧为内存块图,表示实际内存块的空间分布,曲线表示当前内存块的时间占用量。点击该区域,可扩大显示对应内存块。

  • 中间区域中的色块表示横向表示占用时间信息,纵向表示内存占用,红色表示写,绿色表示读,点击色块,可扩大显示,序列图会同步到对应时间位置。

  • 对于LOCAL_MEM,只显示了一个TPU(BM1684一共有64个TPU)的内存空间,占用TPU个数信息可以根据色块信息中shape信息中的C维大小来确定。

4. 静态指令解析说明

利用静态指令解析功能,会根据bmprofile_data里的cmd_xxx_y.dat在out_dir中生成多个cmd_xxx_y.log文本文件,方便查看指令是否有问题,如下面两张图所示,分别显示了一条完整的BD指令和GDMA指令:

---Command #0---
Registers:
  CMD_EN=1
  CMD_END=0
  CMD_ID_EN=1
  CMD_ID_GDMA=5
  CMD_ID_TPU=1
  CMD_INTR_EN=0
  CMD_KEEP=0
  OPD0_ADDR=134479872
  OPD0_C=9
  OPD0_C_STR=256
  OPD0_DN_PAD=0
  OPD0_H=16
  OPD0_H_SHIFT=0
  OPD0_H_STR=16
  OPD0_LF_PAD=1
  OPD0_N=1
  OPD0_N_STR=256
  OPD0_RT_PAD=1
  OPD0_UP_PAD=0
  OPD0_W=16
  OPD0_W_SHIFT=0
  OPD0_W_STR=1
  OPD0_X_INS0=0
  OPD0_Y_INS0=1
  OPD1_ADDR=134545408
  OPD1_C=1
  OPD1_C_STR=10
  OPD1_H=1
  OPD1_H_SHIFT=0
  OPD1_H_STR=9
  OPD1_N=1
  OPD1_NEQ1=0
  OPD1_N_STR=1
  OPD1_W=3
  OPD1_W_SHIFT=0
  OPD1_W_STR=3
  OPD1_X_INS0=0
  OPD1_Y_INS0=0
  OPD2_ADDR=134545444
  OPD2_C_STR=20
  OPD2_H_STR=1
  OPD2_N_STR=1
  OPD2_W_STR=1
  OPD3_ADDR=0
  OPT_KERNEL_ROTATE=0
  OPT_LEFT_SHIFT=0
  OPT_LEFT_TRAN=0
  OPT_OPD0_CONST=0
  OPT_OPD0_PREC=0
  OPT_OPD0_SIGN=1
  OPT_OPD1_CONST=0
  OPT_OPD1_PREC=0
  OPT_OPD1_SIGN=1
  OPT_OPD2_CONST=0
  OPT_OPD2_PREC=3
  OPT_OPD2_SIGN=1
  OPT_OPD3_CONST=0
  OPT_RELU=1
  OPT_RES0_PREC=0
  OPT_RES_ADD=0
  OPT_RIGHT_SHIFT=0
  OPT_SHIFT_TYP=1
  OPT_WINOGRAD=0
  RES0_ADDR=134348800
  RES0_C=256
  RES0_C_STR=1
  RES0_H=16
  RES0_H_SHIFT=0
  RES0_H_STR=1
  RES0_N=1
  RES0_N_STR=1
  RES0_W=16
  RES0_W_SHIFT=0
  RES0_W_STR=1
  RES1_ADDR=0
  RES_ADD_SIGN=0
  RES_OP_X_STR=1
  RES_OP_Y_STR=1
  SHORT_OPD0_STR=3
  SHORT_OPD1_STR=3
  SHORT_OPD2_STR=3
  SHORT_RES0_STR=0
  TSK_EU_TYP=0
  TSK_LANE_NUM_0=4294967295
  TSK_LANE_NUM_1=4294967295
  TSK_OPD_NUM=3
  TSK_TYP=0
  ref_id=0
MemRecords:
  in,  lmem, start_offset=262144[0x40000], end_offset=263165[0x403fd], data_size=9216, cover_size=1021, CONV_NEURON:id=1, input0, pad(0,0,1,1), str(1,1), dilate(1,1), ins0(0,0), INT8, shape=(1,9,16,16), user_stride=(256,256,16,1), cidx=0, cnum=1, elem_size=4, store=INT8_4N
  in,  lmem, start_offset=327680[0x50000], end_offset=327833[0x50099], data_size=27648, cover_size=153, CONV_NEURON:id=1, kernel, INT8, shape=(9,256,1,3), user_stride=(1,10,9,3), cidx=0, cnum=4, elem_size=4, store=INT8_4N
  in,  lmem, start_offset=327716[0x50024], end_offset=327837[0x5009d], data_size=512, cover_size=121, CONV_NEURON:id=1, bias, INT16, shape=(1,256,1,1), user_stride=(1,20,1,1), cidx=0, cnum=4
  out, lmem, start_offset=131072[0x20000], end_offset=135165[0x20ffd], data_size=262144, cover_size=4093, CONV_NEURON:id=1, output, INT8, shape=(1,256,16,16), auto_stride=(1024,256,16,1), cidx=0, cnum=4, elem_size=4, store=INT8_4N
---Command #0---
Registers:
  ACC_WRITE_ENABLE=0
  BARRIER_ENABLE=1
  CHAIN_END=0
  CHW_COPY=0
  CMD_ID=1
  COMMON_MODE=0
  CONSTANT_VALUE=0
  DEBUG_MODE=1
  DES_TYPE=0
  DIRECTION=2
  DST_CSIZE=3
  DST_CSTRIDE=288
  DST_DATA_FORMAT=0
  DST_HSHIFT=0
  DST_HSIZE=16
  DST_HSTRIDE=16
  DST_NSIZE=1
  DST_NSTRIDE=864
  DST_START_ADDR=4380168256
  DST_START_ADDR_H8=1
  DST_START_ADDR_L32=85200960
  DST_WSHIFT=0
  DST_WSIZE=16
  DST_WSTRIDE=1
  ENG0_SYNC_ID=0
  ENG1_SYNC_ID=0
  ENG3_SYNC_ID=0
  HOLD_DES_VALUE=0
  INTR_EN=0
  LOCALMEM_MASK_H32=4294967295
  LOCALMEM_MASK_L32=4294967295
  LRN_SHIFT_DIR=0
  LRN_SHIFT_NUM=1
  PIO_GDMA_ENABLE=1
  PREFETCH_DISABLE=1
  SINGLE_STEP=1
  SPECIAL_FUNC=0
  SRC_CSIZE=3
  SRC_CSTRIDE=256
  SRC_DATA_FORMAT=0
  SRC_HSHIFT=0
  SRC_HSIZE=16
  SRC_HSTRIDE=16
  SRC_NSIZE=1
  SRC_NSTRIDE=768
  SRC_START_ADDR=4379901952
  SRC_START_ADDR_H8=1
  SRC_START_ADDR_L32=84934656
  SRC_WSHIFT=0
  SRC_WSIZE=16
  SRC_WSTRIDE=1
  STRIDE_ENABLE=1
  SYS_MEM_TYPE=0
  ref_id=0
MemRecords:
  in,  gmem, start_addr=4379901952[0x105100000], end_addr=4379905024[0x105100c00], data_size=3072, cover_size=3072
, NORMAL-S2S:id=1, dtype=FLOAT32, shape=(1,3,16,16), stride=(768,256,16,1)
  out, gmem, start_addr=4380168256[0x105141040], end_addr=4380171584[0x105141d40], data_size=3072, cover_size=3328, NORMAL-S2S:id=1, dtype=FLOAT32, shape=(1,3,16,16), stride=(864,288,16,1)
  • — Command #N – 为命令分隔符,N为命令序号

  • Registers 部分显示的是实际寄存器配置值,与BD和GDMA的寄存器表一一对应

  • MemRecords 部分显示的是每条指令访问的内存信息:

    • in表示读,out表示写,

    • gmem表示是global mem,lmem表示是local mem,

    • data_size表示实际数据大小,

    • cover_size表示考虑stride后数据覆盖大小

    • start_addr/start_offset表示gmem/lmem起始地址

    • end_addr/end_offset表示gmem/lmem的结束地址,其值等于起始地址+cover_size

    • 其他信息为描述当前操作的功能及操作数的dtype, shape, stride信息等,以desc变量描述

condition使用说明:

在command模式下,可以使用–test ‘condition’参数查找过滤指令

python -m bmprofile  --mode command bmprofile_data out_dir --test 'condition'

其中,condition必须是一个符合python语法的结果为bool类型的语句,可以使用的变量包括:
  • gdma:为True表示是gdma指令

  • bd:为True表示是bd指令

  • bd和gdma文本指令里Registers里的所有寄存器变量

  • MemRecords是以m数组存在的,有多少输入输出就有多少个元素,每个元素可用属性:is_in,is_out,gmem(bool),lmem(bool),start_addr/start_offset,end_addr/end_offset,data_size,cover_size,desc(str)

使用示例:

python -m bmprofile  --mode command bmprofile_data cmd_out --test 'gdma and CMD_ID>=15 and CMD_ID<=16' #查找gdma指令,且CMD_ID在[15,16]范围内
python -m bmprofile --mode command bmprofile_data-1 cmd_out --test "m[-1].end_addr>=0x105141000" #查找最后一个MemRecords的end_addr大于0x105141000的命令,可以用于检测是否有访问越界指令
python -m bmprofile --mode command bmprofile_data-1 cmd_out --test "all([ mm.cover_size>1024 for mm in m if mm.is_out])" # 查找所有输出的cover_size>1024的指令
python -m bmprofile --mode command bmprofile_data-1 cmd_out --test 'm[-1].desc.find("shape=(1,256,8,8)")>=0' #查找特定shape输出的指令

对于查找到指令,会把整个指令在终端打印出来,而且输出文件中指令分隔标记为" — Command #N, mark for ‘condition’ — ",可以通过 mark for 来查找特定指令

注意事项

目前bmprofile功能还在完善中,界面可能稍与上面示例图片不同