从C程序调用TFLM Invoke函数时钟周期大幅增加的问题
在C项目中集成TensorFlow Lite for Microcontrollers(TFLM),通过编写C++函数封装推理逻辑并供C项目调用,函数代码如下:
extern "C" int tflm_inference_int8(int8_t* numElementsInputTensors, int8_t** inputTensors, const int16_t* inputTensorShapes, int8_t* numElementsOutputTensors, int8_t** outputTensors, const int16_t* outputTensorShapes) { for (int i = 0; i < *numElementsInputTensors; i++) { memcpy(g_interpreter->input(i)->data.int8, inputTensors[i], inputTensorShapes[i] * sizeof(int8_t)); } if (kTfLiteOk != g_interpreter->Invoke()) return 1; for (int i = 0; i < *numElementsOutputTensors; i++) { memcpy(outputTensors[i], g_interpreter->output(i)->data.int8, outputTensorShapes[i] * sizeof(int8_t)); } return 0; }
该函数实现了输入数据拷贝到TFLM解释器输入张量、调用全局解释器g_interpreter的Invoke方法执行推理、将输出张量数据拷贝回指定缓冲区的逻辑。编译运行无错误,但硬件执行时,函数消耗的时钟周期远高于C++仿真中的测量值。
已完成排查:
- 输入输出数据的
memcpy复制工作正常; - 性能分析显示额外时钟周期主要消耗在
g_interpreter->Invoke()调用阶段; - 简化输入数据减小张量尺寸后,时钟周期的增加仍不成比例。
询问:调用TFLM的Invoke函数时时钟周期大幅增加的原因是什么?在C项目中集成TFLM有哪些已知的性能低效点或注意事项可以解释该现象?
可能的原因与优化注意事项
1. 编译优化等级未匹配硬件需求
仿真环境通常默认开启较高等级编译优化(如-O3),但嵌入式C项目可能为调试仅启用-O0或-O1。TFLM核心算子依赖编译优化消除冗余指令、循环展开、优化寄存器分配,低优化等级会让Invoke阶段执行效率急剧下降,尤其是卷积、全连接这类循环密集的算子。
2. 内存访问未对齐
TFLM部分算子实现假设张量数据符合硬件对齐要求(如32位/64位对齐),若C项目中分配的输入/输出缓冲区或TFLM内部张量内存未做对齐处理,硬件会触发非对齐内存访问的额外开销——部分架构会插入对齐修复指令,甚至中断处理,直接导致时钟周期飙升。
3. 未启用硬件加速算子
仿真环境可能使用x86架构的优化算子,但目标硬件上未启用对应平台的TFLM硬件加速后端(如ARM Cortex-M用CMSIS-NN、ESP32用ESP-NN)。默认通用C算子的性能远低于硬件加速版本,Invoke阶段会大量执行低效的通用代码。
4. 全局解释器初始化未做硬件适配
全局解释器g_interpreter初始化时,若未根据目标硬件设置专属内存分配器(如用硬件内存池替代默认malloc),或未启用张量内存静态分配(静态分配内存访问效率远高于动态分配),会导致Invoke阶段频繁触发内存管理额外开销,或拉长内存访问路径。
5. 仿真与硬件的指令集差异
仿真环境可能基于x86的SIMD指令集(如SSE、AVX)加速,但目标硬件指令集(如ARM Cortex-M的Thumb-2、无SIMD指令)不支持这类优化,相同算子逻辑在硬件上需要更多时钟周期完成。此外,部分硬件分支预测能力弱于仿真环境,TFLM中分支密集的算子会产生更多分支预测错误,增加执行周期。
6. 未启用TFLM量化优化细节
虽然使用了int8量化模型,但编译TFLM库时若未开启量化相关编译宏(如TF_LITE_STATIC_MEMORY、针对硬件的指令禁用宏),库中可能保留浮点兼容的冗余逻辑,或未针对int8量化做指令级优化,进而拖慢Invoke执行速度。
内容的提问来源于stack exchange,提问作者Emil Hammer

