文档首页> Atlas 500应用> 应用调优> 要点介绍> 内存管理> Matrix框架提供的内存管理接口
更新时间:2021-03-18 GMT+08:00
分享

Matrix框架提供的内存管理接口

框架单独提供了一套内存分配和释放接口,包括HIAI_DMalloc/HIAI_DFree、 HIAI_DVPP_DMalloc/HIAI_DVPP_DFree,支持C/C++语言。其中,HIAI_DMalloc/HIAI_DFree接口主要用于申请内存,再配合SendData接口从Host侧搬运数据到Device侧;HIAI_DVPP_DMalloc/HIAI_DVPP_DFree接口主要用于申请Device侧DVPP使用的内存。通过调用HIAI_DMalloc/HIAI_DFree、 HIAI_DVPP_DMalloc/HIAI_DVPP_DFree接口申请内存,能够尽量少拷贝,减少流程处理时间。

接口描述

关于HIAI_DMalloc/HIAI_DFree、 HIAI_DVPP_DMalloc/HIAI_DVPP_DFree接口的功能,如表1所示。

表1 接口描述

接口名称

接口功能

HIAIMemory::HIAI_DMalloc(C++专用接口)

申请内存接口, 该内存类似普通内存,在用跨侧传输(Host-Device/Device-Host)以及模型推理处理时,性能更优。

HIAIMemory::HIAI_DFree(C++专用接口)

与HIAIMemory::HIAI_DMalloc配合使用, 用于释放HIAIMemory::HIAI_DMalloc分配的内存。

如果在调用HIAIMemory::HIAI_DMalloc接口时,将flag参数值设置为MEMORY_ATTR_AUTO_FREE,表示如果分配了内存,且通过SendData接口发送数据到对端,则无需调用HIAIMemory::HIAI_DFree,对端接收到数据后,内存会自动释放;如果分配了内存,但没有通过SendData接口发送数据到对端或者通过SendData接口发送数据失败,则需要调用HIAIMemory::HIAI_DFree释放内存。

HIAI_DMalloc(C语言与C++通用接口)

申请内存接口, 该内存类似普通内存,在用跨侧传输(Host-Device/Device-Host)以及模型推理处理时,性能更优。

HIAI_DFree(C语言与C++通用接口)

与HIAI_DMalloc配合使用, 用于释放HIAI_DMalloc分配的内存。

如果在调用HIAI_DMalloc接口时,将flag参数值设置为MEMORY_ATTR_AUTO_FREE,表示如果分配了内存,且通过SendData接口发送数据到对端,则无需调用HIAI_DFree,程序运行结束后,内存会自动释放;如果分配了内存,但没有通过SendData接口发送数据到对端或者通过SendData接口发送数据失败,则需要调用HIAI_DFree释放内存。

HIAIMemory::HIAI_DVPP_DMalloc(C++专用接口)

申请内存接口,该接口主要给Device端分配DVPP使用的内存。

HIAIMemory::HIAI_DVPP_DFree(C++专用接口)

用于释放内存, 该接口主要用于释放HIAIMemory::HIAI_DVPP_DMalloc接口分配的内存。

HIAI_DVPP_DMalloc ( C语言与C++通用接口 )

申请内存接口,该接口主要给Device端分配DVPP使用的内存。

HIAI_DVPP_DFree ( C语言与C++通用接口)

用于释放内存, 该接口主要用于释放HIAI_DVPP_DMalloc接口分配的内存。

接口调用流程

图1 接口调用流程

图1中关于接口使用的说明如下:

  • 通过HIAI_DMalloc或HIAIMemory::HIAI_DMalloc接口申请的内存可用于端到端数据传输以及模型推理时使用。调用HIAI_DMalloc或HIAIMemory::HIAI_DMalloc接口,同时配合使用HIAI_REGISTER_SERIALIZE_FUNC宏(对用户自定义数据类型进行序列化或反序列化),可使数据传输效率更高,性能更优。

    通过HIAI_DMalloc或HIAIMemory::HIAI_DMalloc接口申请内存,有以下优势

    • 申请的内存是可以直接给到HDC做数据搬运的, 这样可以避免Matrix与HDC间的数据拷贝。
    • 申请的内存可以直接使能模型推理零拷贝机制,减少数据拷贝时间。
  • 通过HIAI_DVPP_DMalloc或HIAIMemory::HIAI_DVPP_DMalloc接口申请的内存可以给DVPP使用, 同时也可以在DVPP使用完后透传给模型推理时使用。如果不需要做模型推理,HIAI_DVPP_DMalloc接口申请的内存中的数据可以直接回传给Host侧。
  • 通过HIAI_DMalloc、HIAIMemory::HIAI_DMalloc、HIAI_DVPP_DMalloc、HIAIMemory::HIAI_DVPP_DMalloc接口申请的内存兼容原生语言的内存管理接口, 可以当做普通内存使用,但是不能使用free、delete等来释放。一般来说,通过HIAI_DMalloc、HIAIMemory::HIAI_DMalloc、HIAI_DVPP_DMalloc、HIAIMemory::HIAI_DVPP_DMalloc接口申请的内存,需要分别通过调用HIAI_DFree、HIAIMemory::HIAI_DFree、HIAI_DVPP_DFree、HIAIMemory::HIAI_DVPP_DFree接口释放内存。

    如果在调用HIAI_DMalloc或HIAIMemory::HIAI_DMalloc接口时,将flag参数值设置为MEMORY_ATTR_AUTO_FREE,且通过SendData接口发送数据到对端,则无需调用HIAI_DFree或HIAIMemory::HIAI_DFree接口,对端接收到数据后,内存会自动释放;如果仅将flag参数值设置为MEMORY_ATTR_AUTO_FREE,但没有通过SendData接口发送数据到对端或者通过SendData接口发送数据失败,则需要调用HIAIMemory::HIAI_DFree释放内存。

  • 通过HIAI_DVPP_DMalloc、HIAIMemory::HIAI_DVPP_DMalloc接口申请的内存是满足DVPP要求的内存,所以资源较为有限,建议仅当DVPP处理场景时使用。

接口使用要点

通过HIAI_DMalloc或HIAIMemory::HIAI_DMalloc接口申请内存,关于内存管理,请注意以下要点

  • 申请自动释放内存,用于Host到Device或Device到Host的数据传输时,如果是智能指针,由于Matrix框架自动释放内存,所以智能指针指定的析构器必须是空的;如果非智能指针,则Matrix框架自动释放。
  • 申请手动释放内存,用于Host到Device或Device到Host的数据传输时,如果是智能指针,则需要指定析构器为HIAI_DFree或HIAIMemory::HIAI_DFree;如果非智能指针,则数据发送完成后需要调用HIAI_DFree或HIAIMemory::HIAI_DFree释放内存。
  • 申请自动释放内存,对于该内存中的数据,不允许多次调用SendData接口发送数据。
  • 申请手动释放内存时,如果用于Host到Device或Device到Host的数据传输时,在内存释放前,不可复用内存中的数据;如果用于Host到Host或Device到Device的数据传输时,在内存释放前,可以复用内存中的数据。
  • 申请手动释放内存时,如果调用SendData接口异步传输数据,发送数据后,不允许修改内存中的数据。

如果调用HIAI_DVPP_MAlloc或HIAIMemory::HIAI_DVPP_DMalloc接口申请内存,用于Device到Host的数据传输时,由于HIAI_DVPP_MAlloc或HIAIMemory::HIAI_DVPP_DMalloc没有自动释放标签,所以一定需要调用HIAI_DVPP_DFree或HIAIMemory::HIAI_DVPP_DFree接口手动释放内存。如果使用智能指针存放申请的内存地址, 必须指定析构器为HIAI_DVPP_DFree或HIAIMemory::HIAI_DVPP_DFree。

接口调用示例

(1)使用性能优化方案传输数据,必须对发送数据的接口进行手动序列化和反序列化: 
// 注:序列化函数在发送端使用,反序列化在接收端使用,所以这个注册函数最好在接收端和发送端都注册一遍;
//数据结构 
typedef struct
{
    uint32_t left_offset = 0;
    uint32_t right_offset = 0;
    uint32_t top_offset = 0;
    uint32_t bottom_offset = 0;
    //下面serialize函数用于序列化结构体
    template <class Archive>
    void serialize(Archive & ar)
    {
        ar(left_offset,right_offset,top_offset,bottom_offset);
    }
} crop_rect;



// 注册Engine将流转的结构体
typedef struct EngineTransNew
{
    std::shared_ptr<uint8_t> trans_buff = nullptr;    // 传输Buffer
    uint32_t buffer_size = 0;                   // 传输Buffer大小
    std::shared_ptr<uint8_t> trans_buff_extend = nullptr;
    uint32_t buffer_size_extend = 0;
    std::vector<crop_rect> crop_list;
    //下面serialize函数用于序列化结构体
    template <class Archive>
    void serialize(Archive & ar)
    {
        ar(buffer_size, buffer_size_extend, crop_list);
    }
}EngineTransNewT;
 
//序列化函数 
/**
* @ingroup hiaiengine
* @brief GetTransSearPtr,        序列化Trans数据
* @param [in] : data_ptr         结构体指针
* @param [out]:struct_str       结构体buffer
* @param [out]:data_ptr         结构体数据指针buffer
* @param [out]:struct_size      结构体大小
* @param [out]:data_size        结构体数据大小
*/
void GetTransSearPtr(void* data_ptr, std::string& struct_str,
    uint8_t*& buffer, uint32_t& buffer_size)
{
    EngineTransNewT* engine_trans = (EngineTransNewT*)data_ptr;
    uint32_t dataLen = engine_trans->buffer_size;
    uint32_t dataLen_extend = engine_trans->buffer_size_extend;
    // 获取结构体buffer和size
    buffer_size = dataLen + dataLen_extend;
    buffer = (uint8_t*)engine_trans->trans_buff.get();

    // 序列化处理
    std::ostringstream outputStr;
    cereal::PortableBinaryOutputArchive archive(outputStr);
    archive((*engine_trans));
    struct_str = outputStr.str();
}
 
//反序列化函数 
/**
* @ingroup hiaiengine
* @brief GetTransSearPtr,             反序列化Trans数据
* @param [in] : ctrl_ptr              结构体指针
* @param [in] : data_ptr              结构体数据指针
* @param [out]:std::shared_ptr<void> 传给Engine的指针结构体指针
*/
std::shared_ptr<void> GetTransDearPtr(
    const char* ctrlPtr, const uint32_t& ctrlLen,
    const uint8_t* dataPtr, const uint32_t& dataLen)
{
    if(ctrlPtr == nullptr) {
        return nullptr;
    }
    std::shared_ptr<EngineTransNewT> engine_trans_ptr = std::make_shared<EngineTransNewT>();
    // 给engine_trans_ptr赋值
    std::istringstream inputStream(std::string(ctrlPtr, ctrlLen));
    cereal::PortableBinaryInputArchive archive(inputStream);
    archive((*engine_trans_ptr));
    uint32_t offsetLen = engine_trans_ptr->buffer_size;
    if(dataPtr != nullptr) {
        (engine_trans_ptr->trans_buff).reset((const_cast<uint8_t*>(dataPtr)), ReleaseDataBuffer);
        // 因为trans_buff和trans_buff_extend指向的是一块以dataPtr为首地址的连续内存空间,
        // 因此只需要trans_buff挂载析构器释放一次即可
        (engine_trans_ptr->trans_buff_extend).reset((const_cast<uint8_t*>(dataPtr + offsetLen)), SearDeleteNothing);
    }
    return std::static_pointer_cast<void>(engine_trans_ptr);
}
 
// 注册EngineTransNewT
HIAI_REGISTER_SERIALIZE_FUNC("EngineTransNewT", EngineTransNewT, GetTransSearPtr, GetTransDearPtr);

(2) 在发送数据时,需要使用注册的数据类型,另外配合使用HIAI_DMalloc分配数据内存,可以使性能更优
     注:在从host侧向Device侧搬运数据时,使用HIAI_DMalloc方式将很大的提供传输效率,建议优先使用HIAI_DMalloc,该内存接口目前支持0 – (256M Bytes - 96 Bytes)的数据大小,如果数据超出该范围,则需要使用malloc接口进行分配;
     // 使用Dmalloc接口申请数据内存,10000为时延,为10000毫秒,表示如果内存不足,等待10000毫秒;
     HIAI_StatusT get_ret =  HIAIMemory::HIAI_DMalloc(width*align_height*3/2,(void*&)align_buffer, 10000); 
   
     // 发送数据,调用该接口后无需调用HIAI_DFree接口,10000为时延
     graph->SendData(engine_id_0, "TEST_STR", std::static_pointer_cast<void>(align_buffer), 10000);
分享:

    相关文档

    相关产品