7. 在开发板上运行离线网络模型
本章将通过dla_simulator.cpp文件,针对使用MI_IPU API的顺序举例说明。
说明 | ||
---|---|---|
注意事项 | 建议在开发板上运行离线网络模型之前,使用Simulator对模型进行验证。Simulator能够在PC端模拟板上运行环境。 | |
首先确保开发板已经烧录好最新的固件,使用mi_demo中的dla_simulator可对单张图片推演一次,结果为推演结果的TOP5。 | ||
7.1. 创建IPU设备¶
MI_S32 IPUCreateDevice(char* pFirmwarePath, MI_U32 u32VarBufSize)
{
MI_S32 s32Ret = MI_SUCCESS;
MI_IPU_DevAttr_t stDevAttr;
stDevAttr.u32MaxVariableBufSize = u32VarBufSize;
s32Ret = MI_IPU_CreateDevice(&stDevAttr, NULL, pFirmwarePath, 0);
return s32Ret;
}
(1) 参数说明
① 输入参数:
pFirmwarePath:在该款chip上不再需要firmware,因此传NULL
u32VarBufSize:模型内部Tensor使用的memory的最大大小
方法1 :可以通过parse_net工具获取
parse_net工具路径:SGS_IPU_SDK/bin/parse_net
使用时直接在parse_net命令后加上定点网络模型路径即可,可参考以下命令:
./parse_net xxx_fixed.sim | grep Variable
输出:
| |--> SubGraph[0]: Variable buffer 0xd2f00 @ 0x82f3b40.
其中0xd2f00向2对齐后为u32VarBufSize大小。
在C/C++中,向2对齐的定义为:
#define alignment_up(a, size) ((a + size - 1) & (~(size - 1)))
所以u32VarBufSize为864000。
方法2 :使用MI的API获取
MI_IPU_OfflineModelStaticInfo_t OfflineModelInfo;
ret = MI_IPU_GetOfflineModeStaticInfo(NULL, modelFilePath, &OfflineModelInfo);
if (ret != MI_SUCCESS)
{
std::cerr << "get model variable buffer size failed!" << std::endl;
return;
}
u32VarBufSize = OfflineModelInfo.u32VariableBufferSize
如果有多个模型需要运行,选取最大的u32VarBufSize创建IPU设备即可。
② 输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API_V3.DOC》
7.2. 创建IPU通道¶
MI_S32 IPUCreateChannel(MI_U32* u32Channel, char* pModelImage)
{
MI_S32 s32Ret ;
MI_IPUChnAttr_t stChnAttr;
//create channel
memset(&stChnAttr, 0, sizeof(stChnAttr));
stChnAttr.u32InputBufDepth = 2;
stChnAttr.u32OutputBufDepth = 2;
return MI_IPU_CreateCHN(u32Channel, &stChnAttr, NULL, pModelImage);
}
MI_U32 u32Channel;
ret = IPUCreateChannel(&u32ChannelID, “./mobilenet_v1_fixed.img”);
① 输入参数:
u32Channel:创建IPU通道的ID
pModelImage:离线网络模型文件路径离线
如果需要支持批处理输入,请参考dla_simulator_nbatch.cpp
,示例如下:
MI_S32 IPUCreateChannel(MI_U32* u32Channel, char* pModelImage, int number) { MI_SYS_GlobalPrivPoolConfig_t stGlobalPrivPoolConf; MI_IPUChnAttr_t stChnAttr; //create channel memset(&stChnAttr, 0, sizeof(stChnAttr)); stChnAttr.u32InputBufDepth = 1; stChnAttr.u32OutputBufDepth = 1; stChnAttr.u32BatchMax = number; return MI_IPU_CreateCHN(u32Channel, &stChnAttr, NULL, pModelImage); }
输入参数:
u32Channel:创建IPU通道的ID pModelImage:离线网络模型文件路径离线 number:批处理输入batch数
② 输出参数:
MI_IPU API 错误码,具体参考《MI_IPU_API_V3.DOC》
7.3. 获取模型输入输出Tensor属性¶
MI_IPU_SubNet_InputOutputDesc_t desc;
MI_IPU_GetInOutTensorDesc(u32ChannelID, &desc);
(1) 参数说明
① 输入参数:
u32ChnId:IPU通道的ID
pstDesc:IPU子网络输入/输出描述结构体
② 输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API_V3.DOC》
7.4. 获取输入输出Tensor¶
MI_IPU_TensorVector_t InputTensorVector;
MI_IPU_TensorVector_t OutputTensorVector;
MI_IPU_GetInputTensors(u32ChannelID, &InputTensorVector);
MI_IPU_GetOutputTensors(u32ChannelID, &OutputTensorVector);
(1) 参数说明
① 输入参数:
u32ChannelID:IPU通道的ID
InputTensorVector:输入IPU Tensor数组结构体
OutputTensorVector:输出IPU Tensor数组结构体
如果需要支持批处理输入,请参考dla_simulator_nbatch.cpp
,示例如下:
MI_IPU_BatchInvokeParam_t stInvokeParam; memset(&stInvokeParam, 0, sizeof(MI_IPU_BatchInvokeParam_t)); stInvokeParam.u32BatchN = number; MI_IPU_GetInputTensors2(u32ChannelID, &stInvokeParam); MI_IPU_GetOutputTensors2(u32ChannelID, &stInvokeParam);
输入参数:
u32ChannelID:IPU通道的ID stInvokeParam:invoke参数结构体,包括批处理输入batch数,以及输入输出tensors的指针
② 输出参数:
MI_IPU API 错误码,具体参考《MI_IPU_API_V3.DOC》
7.5. 模型数据输入¶
7.5.1. 拷贝数据¶
将数据拷贝至模型输入Tensor的虚拟地址,拷贝完成后调用MI_SYS_FlushInvCache:
MI_U8* pdata = (MI_U8 *)InputTensorVector.astArrayTensors[0].ptTensorData[0];
MI_U8* pSrc = (MI_U8 *)Input_Data;
for(int i = 0; i < imageSize; i++)
{
*(pdata + i) = *(pSrc + i);
}
MI_SYS_FlushInvCache(pdata, imageSize);
-
注意:ptTensorData / phyTensorAddr 只用到0地址。
-
模型中input formats设置为
BGR
或者RGB
时,stride不要做alignment,做了alignment反而有问题。 -
模型中input formats设置为
BGRA
或者RGBA
(A channel在高地址),不需要alignment。input_formats设置为
BGRA
,对应MI_SYS_PixelFormat_e为E_MI_SYSPIXEL_FRAME_ARGB8888
。input_formats设置为
RGBA
,对应MI_SYS_PixelFormat_e为E_MI_SYSPIXEL_FRAME_ABGR8888
。 -
input formats设置为
YUV_NV12
或者GRAY
时,alignment规则是stride = alignment_up(width, 2),height需要2 alignment。 -
input_formats设置为
YUV_NV12
或者GRAY
时,对应MI_SYS_PixelFormat_e为E_MI_SYSPIXEL_FRAME_YUV_SEMIPLANAR_420
。
7.5.2.零拷贝数据¶
如果使用MI的其他模块,为模型输入做好了数据准备,可以不用拷贝数据,直接给传递其他MI模块的物理地址(该起始物理地址必须要是64 bytes对齐)。
以下以MI_SYS模块示例模型输入零拷贝数据,需注意:
创建IPU通道时,输入InputBufDepth设为零,不再使用MI_IPU_GetInputTensors分配输入空间
stChnAttr.u32InputBufDepth = 0;
由于不使用MI_IPU_GetInputTensors,需手动将网络描述的网络模型输入个数赋给
InputTensorVector.u32TensorCount。
InputTensorVector.u32TensorCount = desc.u32InputTensorCount;
使用MI_SYS的API分配空间:
MI_S32 s32ret = 0;
MI_PHY phyAddr = 0;
void* pVirAddr = NULL;
s32ret = MI_SYS_MMA_Alloc(NULL, BufSize, &phyAddr);
if (s32ret != MI_SUCCESS)
{
throw std::runtime_error("Alloc buffer failed!");
}
s32ret = MI_SYS_Mmap(phyAddr, BufSize, &pVirAddr, TRUE);
if (s32ret != MI_SUCCESS)
{
MI_SYS_MMA_Free(phyAddr);
throw std::runtime_error("Mmap buffer failed!");
}
传递虚拟地址和对应的起始物理地址(该起始物理地址必须要是64 bytes对齐)给InputTensorVector
InputTensorVector.astArrayTensors[0].ptTensorData[0] = pVirAddr;
InputTensorVector.astArrayTensors[0].phyTensorAddr[0] = phyAddr;
之后可以进行模型推演。 使用零拷贝数据,不需要再释放输入Tensor,即不再调用MI_IPU_PutInputTensors函数,释放Buffer调用MI_SYS的API完成。 释放MI_SYS的Buffer
MI_SYS_Munmap(pVirAddr, BufSize);
MI_SYS_MMA_Free(phyAddr);
7.6. 模型推演¶
ret = MI_IPU_Invoke(u32ChannelID, &InputTensorVector, &OutputTensorVector);
if (ret != MI_SUCCESS)
{
MI_IPU_DestroyCHN(u32ChannelID);
MI_IPU_DestroyDevice();
std::cerr << "IPU invoke failed!!" << std::endl;
}
① 输入参数:
u32ChannelID:IPU通道的ID
InputTensorVector:输入IPU Tensor数组结构体
(每个输入tensor的起始物理地址必须要是64 bytes对齐)
OutputTensorVector:输出IPU Tensor数组结构体
(每个输出tensor的起始物理地址必须要是64 bytes对齐)
如果需要支持批处理输入,请参考dla_simulator_nbatch.cpp
ret = MI_IPU_Invoke2(u32ChannelID, &stInvokeParam, &stRuntimeInfo); if (ret != MI_SUCCESS) { MI_IPU_DestroyCHN(u32ChannelID); MI_IPU_DestroyDevice(); std::cerr << "IPU invoke failed! Error Code: " << ret << std::endl; return; }
输入参数:
u32ChannelID:IPU通道的ID stInvokeParam:invoke参数结构体,包括批处理batch数、输入输出tensor指针、IPU core选择、 task优先级等 (每个输入/输出tensor的起始物理地址必须要是64 bytes对齐) stRuntimeInfo:模型推演信息结构体,包括推演时间以及推演带宽。
② 输出参数:
MI_IPU API 错误码,具体参考《MI_IPU_API_V3.DOC》
7.7. 释放输入输出Tensor¶
MI_IPU_PutInputTensors(u32ChannelID, &InputTensorVector);
MI_IPU_PutOutputTensors(u32ChannelID, &OutputTensorVector);
(1) 参数说明
① 输入参数:
u32ChannelID:IPU通道的ID
InputTensorVector:输入IPU Tensor数组结构体
OutputTensorVector:输出IPU Tensor数组结构体
如果需要支持批处理输入,请参考dla_simulator_nbatch.cpp
MI_IPU_PutInputTensors2(u32ChannelID, &stInvokeParam); MI_IPU_PutOutputTensors2(u32ChannelID, &stInvokeParam);
输入参数:
u32ChannelID:IPU通道的ID stInvokeParam:invoke参数结构体,包括批处理输入batch数,以及输入输出tensors的指针
② 输出参数:
MI_IPU API 错误码,具体参考《MI_IPU_API_V3.DOC》
7.8. 销毁IPU通道¶
MI_IPU_DestroyCHN(u32ChannelID);
① 输入参数:
s32Channel:创建IPU通道的ID
② 输出参数:
MI_IPU API 错误码,具体参考《MI_IPU_API_V3.DOC》
7.9. 销毁IPU设备¶
MI_IPU_DestroyDevice();
(1) 参数说明
① 输入参数:
空
② 输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API_V3.DOC》