Skip to content

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的最大大小
其中,u32VarBufSize获取方法如下:
    方法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.
         其中0xd2f002对齐后为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);
(1) 参数说明

① 输入参数:

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;
}
(1) 参数说明

① 输入参数:

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);
(1) 参数说明

① 输入参数:

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》