昇腾学院 | Atlas性能调优之内存管理

举报
Mandyy 发表于 2020/04/13 14:41:49 2020/04/13
【摘要】 前面三节课,我们分享了Atlas性能调优之瓶颈分析、流程编排以及编解码的内容,今天,我们将为大家带来Atlas性能调优之内存管理的分享!

作者:熊文博


前面三节课,我们分享了Atlas性能调优之瓶颈分析流程编排以及编解码的内容,今天,我们将为大家带来Atlas性能调优之内存管理的分享!


相对于其他编程语言,C/C++在内存的管理上更为灵活,这给程序带来了更高的性能,但用户需要自己管理内存,比较容易掉入各种陷阱中。本文将介绍下在开发AI应用的时候,如何进行安全高效的内存管理。

Atlas 200 DK上是昇腾310处理器做主控,程序直接运行在处理器上,只需要考虑处理器上的内存管理即可。 

Atlas 300、500等,使用Matrix框架开发的程序是分成两部分的,一部分跑在host侧,一部分跑在device侧,两者之间通过框架的HDC通道进行通信。 

Host侧程序其实就是跑在主机的C/C++程序,这块的内存管理暂且不谈。下面主要说下内存的跨侧传输和Device侧(昇腾310处理器)的内存管理。




跨侧传输



数据的跨侧传输,需要将数据类型序列化之后,再整体发送到对侧,对侧接收到数据后,进行反序列化得出原有数据。


Matrix框架中,数据跨侧传输有普通通道和高速通道两种方式。对于小于256KB的数据,建议使用普通通道,而对于大于等于256KB的数据,建议使用高速通道。


注意:定于数据结构时,对于需要跨侧传输的buffer,请使用智能指针。


普通通道

Matrix的普通通道使用的cereal开源库,用户只需要简单的在数据结构下定义下序列化函数,再使用HIAI_REGISTER_DATA_TYPE注册下该数据结构即可。


使用new申请完内存buffer后,需使用智能指针指向该buffer,指定内存自动释放方法为delete。如以下代码所示:

 

uint8_t* buffer = newuint8_t[bufferSize];
transData->buf.reset(buffer, [](uint8_t* p) { delete[] p; });


高速通道

高速通道走的是DMA搬运,基本上可以达到PCIE通道的理论极限值。高速通道的实现比普通通道略复杂点,需要实现自定义的序列化函数(GetSerializeFunc)和反序列化函数(GetDeserializeFunc),再使用HIAI_REGISTER_SERIALIZE_FUNC注册该数据结构和对应的序列化反序列化方法。


高速通道可使用HIAI_DMalloc来申请内存,注意对于跨侧传输的数据,HIAI_DMalloc申请的内存会自动释放掉,设置释放方法为空,如以下代码


uint8_t* buffer;
hiai::HIAIMemory::HIAI_DMalloc(bufferSize, (void*&)buffer, 10000);
transData->buf.reset(buffer, [](uint8_t* p){});



Device 侧内存管理



内存消耗

昇腾310处理器侧总共有8G内存,由于310处理器是一颗SOC处理器,片上系统大概占用0.5G左右内存,所以可用内存总共有7.5G左右。其中包含1.9G左右的大页内存。大页内存是提前预留好的,所以没有程序运行时,使用npu-smi info看到已经使用的内存为2.4G左右,其实只有系统占用了0.5G1.9G的大页内存还可以使用。


处理器侧资源有限,我们来看下有哪些优化点可以节省内存的消耗。


1、DVPP

昇腾310处理器上面包含了多个硬件模块,其中DVPP比较特殊,原本是一个32位的硬件编解码模块,其中大部分所以需要使用的内存必须在4G空间内。该范围的内存可以使用HIAI_DVPP_DMalloc来申请,HIAI_DVPP_DMalloc接口会优先申请之前预留好的大页内存(2M),大页内存申请完之后,会申请普通内存。注意,申请的内存buffer需要使用HIAI_DVPP_DFree来释放。


由于有这个限制在,而编解码又是内存消耗大户,在某些压力场景下,会出现4G空间用完的情况,有以下优化方案。


VDEC的输入不要求在4G空间内,而VDEC的输入一般是跨侧传输过来的,只需要设置VDEC Engine对应的connectsreceivememorywithout_dvpp1就可以保证搬运来的数据不在4G空间内。


如果是一次编解码操作需要申请多个小块4G空间内存,建议申请一块大内存,通过地址偏移完成。 例如,VPC抠图,如果一张图里面扣10个人脸小图,如果每一个输出都单独申请内存,比如小图一张100KB,如果是大页内存,对齐后为2MB占,这样总共占用2*10=20MB,但是如果一次性申请一块2M内存,地址偏移就可以满足了,这样便可以节约了18MB内存。


2、模型

模型是另外一个内存消耗点,模型占用内存固定,可以通过omg工具,使用以下命令生成json文件


omg --mode=1 --om=XXX.om --json=xxx.json


查看生成的json文件,获取memorysizeweightsize值。那么整个模型的消耗大约为memorysize/1024/1024 + weightsize/1024/1024 + (30MB~60MB),括号中的内存消耗,和batch数有关,单batch大约为30MB左右,8batch60MB左右。


另外,对于推理业务,建议只使用一个Graph,否则多个Graph,每个都加载一遍模型会占用大量内存的。


3、框架

前面文章说过,Engine队列长度,默认值200,上一个Engine发送过来的数据,都会存储在这个队列中。当Engine处理不及时,就会造成数据堆积,一个解码后的图片2M,队列堆满便会总共消耗400M内存。因此业务开发中,需要考虑这种情况,调整适当的队列大小,当出现异常情况导致的队列堆积时,不会把Device侧内存消耗光。



内存拷贝

Matrix框架提供了HIAI_DMalloc / HIAI_DFree 和 HIAI_DVPP_DMalloc / HIAI_DVPP_DFree这两对内存接口。HIAI_DMalloc是用于跨侧传输的,HIAI_DVPP_DMalloc是用于申请DVPP的4G空间的内存的。使用不同接口申请的内存,不需要显示的进行拷贝,可以直接使用,主要点如下:


1、通过HIAI_DMalloc接口申请的内存,作为图像/视频编解码的输入使用,无需进行数据拷贝。


2、HIAI_DVPP_DMalloc接口申请的内存,内存地址满足DVPP的输入/输出要求,可直接作为图像/视频输出的使用。调用HIAI_DVPP_DMalloc接口申请内存后,HIAI_DVPP_DFree接口释放内存。在DVPP内部,VPC模块的输入可直接复用内存中JPEGD模块的输出数据。


3、通过HIAI_DVPP_DMalloc接口申请的内存,可直接作为模型推理首层的输入,无需进行数据拷贝。








总体而言,Matrix内存管理上不算复杂,记住按要求使用框架提供的接口,避开不必要的内存拷贝,就能提高应用程序的运行效率。



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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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