Host-Device数据传输
// EngineTransNewT结构体 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] : dataPtr 结构体指针 * @param [out]:structStr 结构体buffer * @param [out]:buffer 结构体数据指针buffer * @param [out]:buffSize 结构体数据大小 */ 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] : ctrlPtr 结构体指针 * @param [in] : ctrlLen 数据结构中控制信息大小 * @param [in] : dataPtr 结构体数据指针 * @param [in] : dataLen 结构中数据信息存储空间大小,仅用于校验,不表示原始数据信息大小 * @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);
在较大图像数据或码流传输的情况下,用HIAI_REGISTER_SERIALIZE_FUNC对自定义数据类型进行序列化/反序列化,可以实现高性能数据传输,节省传输时间。
Matrix通过“控制信息 + 数据信息”的形式描述要传输的数据,控制信息指用户自定义的数据类型,数据信息指需要传输的数据内容。为保证Host和Device之间的数据传输,Matrix提供如下机制:
- 在传输数据前,用户可调用HIAI_REGISTER_SERIALIZE_FUNC宏注册用户自定义数据类型、用户自定义序列化函数、用户自定义反序列化函数。
- 用户在本端调用SendData接口发送数据后,Matrix会做如下处理,处理流程如图1所示:
- 调用用户自定义的序列化函数对控制信息序列化,将序列化后的控制信息放入内存(ctrlBuf)。
- 通过DMA(Direct Memory Access)映射将控制信息拷贝一份存放到对端的内存中,并保持本端与对端之间控制信息的映射关系。
指向数据信息的内存(dataBuf)指针已通过SendData接口的入参传入,dataBuf是由用户调用HIAI_DMalloc/HIAI_DVPP_DMalloc接口申请的,申请该内存后系统将本端的数据信息通过DMA映射拷贝一份存放到对端的内存中,并保持本端与对端之间数据信息的映射关系。
- 拼装消息发送给对端,主要将ctrlBuf的地址及大小、dataBuf的地址及大小发送到对端。
- 对端收到消息后,Matrix会调用用户自定义的反序列化函数解析对端已获取到的控制信息和数据信息,并将解析后的数据发送给对应的接收Engine进行处理。
- 对端解析数据后,控制信息已使用完成,因此可以释放掉存放控制信息的内存(ctrlBuf),但由于存放控制消息的内存是在本端申请的,因此对端需要给本端发送释放ctrlBuf的消息。
- 本端收到消息后,释放ctrlBuf。
- Engine在收到数据并完成所有处理后,便可以释放dataBuf了,但由于Matrix并不知道用户何时会使用完dataBuf,因此需要用户在实现反序列化函数时,dataBuf以智能指针返回并绑定析构器hiai::Graph::ReleaseDataBuffer。当智能指针结束生命周期析构时,便会自动调用析构器给本端发送释放dataBuf内存消息。
- 本端收到消息后,释放dataBuf。
示例场景:以下代码声明了序列化/反序列化函数,自定义了数据类型(结构体),并将自定义数据类型、序列化函数、反序列化函数通过HIAI_REGISTER_SERIALIZE_FUNC宏注册。在数据传输发起侧的数据传输前,框架调用注册的序列化函数,在数据接收侧的数据传输后,调用注册的反序列化函数。
// EngineTransNewT结构体 struct EngineTransNewT { std::shared_ptr<uint8_t> transBuff; uint32_t bufferSize; // buffer大小 std::string url; //下面serialize函数用于序列化结构体 template <class Archive> void serialize(Archive & ar) { ar(url); } } /** * @ingroup hiaiengine * @brief GetTransSearPtr, 序列化Trans数据 * @param [in] : dataPtr 结构体指针 * @param [out]:structStr 结构体buffer * @param [out]:buffer 结构体数据指针buffer * @param [out]:buffSize 结构体数据大小 */ void GetTransSearPtr(void* dataPtr, std::string& structStr, uint8_t*& buffer, uint32_t& bufferSize) { EngineTransNewT* engineTrans = (EngineTransNewT *)dataPtr; std::shared_ptr<uint8_t> transBuff = ((EngineTransNewT *)dataPtr)->transBuff; buffer = (uint8_t*)engineTrans->transBuff.get(); bufferSize = engineTrans->bufferSize; engineTrans->transBuff = nullptr; engineTrans->buffSize = 0; std::ostringstream outputStr; cereal::PortableBinaryOutputArchive archive(outputStr); archive((*engineTrans)); struct_str = outputStr.str(); ((EngineTransNewT*)dataPtr)->transBuff = transBuff; engineTrans->buffSize = bufferSize; } /** * @ingroup hiaiengine * @brief GetTransSearPtr, 反序列化Trans数据 * @param [in] : ctrlPtr 结构体指针 * @param [in] : ctrlLen 数据结构中控制信息大小 * @param [in] : dataPtr 结构体数据指针 * @param [in] : dataLen 结构中数据信息存储空间大小,仅用于校验,不表示原始数据信息大小 * @param [out]:std::shared_ptr<void> 传给Engine的指针结构体指针 */ std::shared_ptr<void> GetTransDearPtr(char* ctrlPtr, const uint32_t& ctrlLen, uint8_t* dataPtr, const uint32_t& dataLen) { std::shared_ptr<EngineTransNewT> engineTransPtr = std::make_shared<EngineTransNewT>(); std::istringstream inputStream(std::string(ctrlPtr, ctrlLen)); cereal::PortableBinaryInputArchive archive(inputStream); archive((*engineTransPtr)); engineTransPtr->bufferSize = dataLen; engineTransPtr->transBuff.reset(dataPtr, hiai::Graph::ReleaseDataBuffer); return std::static_pointer_cast<void>(engineTransPtr); } // 注册EngineTransNewT HIAI_REGISTER_SERIALIZE_FUNC("EngineTransNewT", EngineTransNewT, GetTransSearPtr, GetTransDearPtr);