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¶
在编译时,开启enable_profile选项(可选,如果没有加,则profile显示时没有相关层的关联信息)
bmnetx命令行参数加上
--enable_profile=True
bmlang在调用编译接口时,令
enable_profile=1
在实际设备上运行bmodel(如bmrt_test、调用bmruntime运行接口)前,设置环境变量
export BMRUNTIME_ENABLE_PROFILE=1
运行runtime程序, 运行结束后在运行目录下生成bmprofile_data-N文件夹,里面包含二进制的profile数据。其中N为设备id号,如设备id为1,则输出目录为bmprofile_data-1
利用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指令) (使用说明见”静态指令解析说明”章节)
如不再使用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¶
在bmkernel的host程序运行前设置环境变量:
export BMLIB_ENABLE_ALL_PROFILE=1
运行bmkernel的host程序,在当前目录生成bmprofile_data-N文件夹
- 运行命令
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观察结果 (使用说明见”数据显示说明”章节)
- 运行命令
如不再使用profile功能,请清除环境变量,以免影响运行速度
unset BMLIB_ENABLE_ALL_PROFILE
- 注意事项:
- 使能profile运行tpu kernel 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. 数据显示说明¶
序列图标号功能说明:
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条,点击表头可按对应列排序,点击数据行可以在时序图中快速定位该数据位置。最后一列表示实际占用时间
内存图显示说明
内存图上方曲线为读写曲线,红色表示写,绿色表示读,黑色表示读写总量。含义是当前时点,读写发生的总内存大小,用于查找内存操作的时间区域
左侧为内存块图,表示实际内存块的空间分布,曲线表示当前内存块的时间占用量。点击该区域,可扩大显示对应内存块。
中间区域中的色块表示横向表示占用时间信息,纵向表示内存占用,红色表示写,绿色表示读,点击色块,可扩大显示,序列图会同步到对应时间位置。
对于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功能还在完善中,界面可能稍与上面示例图片不同