wenet onnxruntime推理内存泄露排查
近日wenet社区公布了onnx推理方案,与原生libtorch相比,在接近无损的情况下,将推理速度提升了15%左右。于是,笔者快速开箱验证,发现了几处需要改进的大坑,与各位看官分享。
onnx模型的导出
在我们用pytorch训练完一个模型之后,需要在导出onnx模型,以便后续的onnxruntime推理。此处笔者使用的额外三方库有:
- onnx 1.11.0
- onnxruntime 1.11.0
仓库地址:https://github.com/wenet-e2e/wenet/tree/c5183c87420f0adfa8ef9946aee696cb5de9f555
执行以下代码进行导出
python wenet/bin/export_onnx_cpu.py --config train.yaml --checkpoint model_path.pt --output_dir exp/ --chunk_size -1 --num_decoding_left_chunks -1 --reverse_weight 0 --cmvn_file global_cmvn #代码1
- 注 笔者新增了cmvn_file选项,以达到能传入自定义的cmvn文件的目的,新增部分代码如下
# 定义parser部分加入一行
parser.add_argument('--cmvn_file', required=False, default='', type=str,
help='global_cmvn file, default path is in config file')
#----------------------------------------------------------------------------
# 在configs之后加入一行
if args.cmvn_file and os.path.exists(args.cmvn_file):
configs['cmvn_file'] = args.cmvn_file
然而,成功并非来的那么容易,执行上述代码后出现以下错误:
按照错误提示,我们做了如下改动(即,为每个InferenceSession指定cpu provider):
然后顺利导出了encoder.onnx/ctc.onnx/decoder.onnx。撒花!~~
内存泄露
导出完成之后,即可进行runtime推理,然后单线程RTF大致在0.05左右。然而,一切美好的事物可能都是个“带刺的玫瑰”。笔者习惯性的用valgrind内存检测工具进行了内存泄露扫描,What??泄露了21处??
仔细观察日志发现,内存泄露主要分两类:
第一类,显示的报告了泄露的位置
这就比较好办了,找到对应的函数GetInputOutputInfo,发现代码34行使用allocator申请了资源,但是并未释放
于是,尝试在OnnxAsrModel的析构函数中添加释放节点名的操作,具体如下:
再重新编译运行代码,又出现内存重复释放的错误:
我明明在释放内存之前,提前判断指针是否存在了呀!!!! 经过反复定位和思考,把矛头指向了asr_decoder中的构造函数
在之前的版本,model一直是不可复制,但是该版本,每new一个decoder都得创建一个新的model(具体原因未可知,后续再分析)。而model的拷贝构造函数,并未对encoder_in_names、encoder_out_names_等资源进行深拷贝,导致了指着的重复释放。
此处有两种选择,一种是在拷贝构造函数中,new出新的资源,然后通过memcpy/strcpy把节点名拷贝过去。然而该方法过于浪费内存资源,于是笔者选择了第二种方法——用string标准库。
利用获取的指向InputName的指针p,初始化string;为了防止这个string超出作用域后,被自动销毁,在OnnxAsrModel中新建一个私有属性all_names_。具体如下:
因为string有自动销毁资源的机制,所以上面的OnnxAsrModel的析构函数也可以删掉啦。
最后执行valgrind内存检测,发现第一类的泄露日志已经不再,之前21处泄露降低到现在的11处:
第二类泄露,未显然暴露泄露位置
具体日志如下,总共有11处泄露,日志样式几乎一样。
-
首先排查是否在神经网络推理阶段存在泄露。于是注释掉75~78行代码,仅加载模型,并用valgrind去检测内存,发现泄露日志依然存在。
-
注释掉66-67行代码,排查decoder创建过程是否存在泄露。然而,泄露日志还是在。
-
最后我们把泄露位置指向了模型资源创建和读取环节,
再联想到泄露的日志,一共有11处,恰好等于模型meta信息的个数。于是,真相逐渐浮出水面。
以上代码中,用allocator申请了资源,但并未释放。应该改成如下方式:
终结
在解决以上两类泄露之后,再用valgrind检测,发现已经完全消除所有内存泄露啦:
- 点赞
- 收藏
- 关注作者
评论(0)