wenet onnxruntime推理内存泄露排查

举报
ASR-beginer 发表于 2022/05/30 17:13:01 2022/05/30
【摘要】 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

然而,成功并非来的那么容易,执行上述代码后出现以下错误:
image-20220530151951064.png
按照错误提示,我们做了如下改动(即,为每个InferenceSession指定cpu provider):
image-20220530152106088.png

然后顺利导出了encoder.onnx/ctc.onnx/decoder.onnx。撒花!~~

内存泄露

导出完成之后,即可进行runtime推理,然后单线程RTF大致在0.05左右。然而,一切美好的事物可能都是个“带刺的玫瑰”。笔者习惯性的用valgrind内存检测工具进行了内存泄露扫描,What??泄露了21处??
image-20220530152728737.png
仔细观察日志发现,内存泄露主要分两类:

第一类,显示的报告了泄露的位置

image-20220530152835527.png
这就比较好办了,找到对应的函数GetInputOutputInfo,发现代码34行使用allocator申请了资源,但是并未释放
image-20220530153155485.png

于是,尝试在OnnxAsrModel的析构函数中添加释放节点名的操作,具体如下:
image-20220530153300282.png
再重新编译运行代码,又出现内存重复释放的错误:
image-20220530153541729.png
我明明在释放内存之前,提前判断指针是否存在了呀!!!! 经过反复定位和思考,把矛头指向了asr_decoder中的构造函数

image-20220530153705643.png
在之前的版本,model一直是不可复制,但是该版本,每new一个decoder都得创建一个新的model(具体原因未可知,后续再分析)。而model的拷贝构造函数,并未对encoder_in_names、encoder_out_names_等资源进行深拷贝,导致了指着的重复释放。

此处有两种选择,一种是在拷贝构造函数中,new出新的资源,然后通过memcpy/strcpy把节点名拷贝过去。然而该方法过于浪费内存资源,于是笔者选择了第二种方法——用string标准库。

利用获取的指向InputName的指针p,初始化string;为了防止这个string超出作用域后,被自动销毁,在OnnxAsrModel中新建一个私有属性all_names_。具体如下:
image-20220530164530439.png
因为string有自动销毁资源的机制,所以上面的OnnxAsrModel的析构函数也可以删掉啦。

最后执行valgrind内存检测,发现第一类的泄露日志已经不再,之前21处泄露降低到现在的11处:
image-20220530164942895.png

第二类泄露,未显然暴露泄露位置

具体日志如下,总共有11处泄露,日志样式几乎一样。
image-20220530165104557.png

  • 首先排查是否在神经网络推理阶段存在泄露。于是注释掉75~78行代码,仅加载模型,并用valgrind去检测内存,发现泄露日志依然存在。
    image-20220530165445278.png

  • 注释掉66-67行代码,排查decoder创建过程是否存在泄露。然而,泄露日志还是在。
    image-20220530165548629.png

  • 最后我们把泄露位置指向了模型资源创建和读取环节,
    image-20220530165649819.png

再联想到泄露的日志,一共有11处,恰好等于模型meta信息的个数。于是,真相逐渐浮出水面。
image-20220530165938206.png
以上代码中,用allocator申请了资源,但并未释放。应该改成如下方式:
image-20220530170102292.png

终结
在解决以上两类泄露之后,再用valgrind检测,发现已经完全消除所有内存泄露啦:
image-20220530170218399.png

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。