YOLO:OpenMP评估 - zhonglong/TPV GitHub Wiki

OpenMP入门

OpenMP 概述
https://software.intel.com/zh-cn/blogs/2012/05/07/openmp-4
OpenMP编程指南
https://blog.csdn.net/drzhouweiming/article/details/4093624

采用OpenMP多核编程框架,只需要在可并行执行的代码(最常见的是循环)前加入“#pragma omp xxx”标志,编译器会根据配置文件自动生成多线程(启用OpenMP)和单线程(禁用OpenMP)的代码。

Darknet中的OpenMP

Darknet源代码已经支持OpenMP,对于Makefile方式编译,只要打开源码目录下的Makefile文件,修改“OPENMP=1”即可。 对于Android NDK方式编译,则需要添加-fopenmp标志,具体来说有两种方式:

  • 修改build.gradle,在externalNativeBuild.cmake.cFlags添加“-fopenmp”标志
  • 修改CMakeLists.txt文件,在CMAKE_C_FLAGS添加“-fopenmp”标志

Darknet源代码采用“#pragma omp parallel for”来优化循环,可以带来明显的性能提升。启用OpenMP的代码,执行network_predict函数,X588从11秒多提升到4秒左右,锤子M1L从2秒多提升到1.1秒多。

Darknet性能瓶颈

根据OpenMP优化,可以推断出Darknet的性能瓶颈在矩阵乘法运算,具体来说是gemm.c文件的四个函数:

  • gemm_nn
  • gemm_tn
  • gemm_nt
  • gemm_tt

矩阵乘法更多说明,请参考:

darknet深度学习框架源码分析:详细中文注释,涵盖框架原理与实现语法分析
https://github.com/hgpvision/darknet

采用OpenMP就是来优化这四个函数的执行时间,受限于CPU核数,电源管理等因素,纯CPU方式进一步优化的空间有限。

Toolchain对OpenMP的影响

Android Studio的CMake工具链可以设置选择gcc或者clang编译器(ANDROID_TOOLCHAIN),不同编译器下OpenMP的表现有所不同。 OpenMP只有gcc编译器下才能带来性能提升,在clang编译器下带来的是负优化。如下表:

# 禁用OpenMP 启用OpenMP
gcc 11秒左右 4秒左右
clang 9秒多 45秒多

另外,gcc和clang对编译参数也有所不同,某些参数只能在clang下使用,例如“-mfpu=neon-vfpv4”。

OpenMP技术分析

OpenMP可以帮助开发人员快速实现并行代码,提升性能;但同时隐藏了实现细节,导致分析起来比较麻烦。 而且使用“#pragma omp parallel for”并不一定总是带来优化,例如嵌套循环会带来负优化,要多做几次尝试。

OpenMP: OpenMP嵌套并行
https://blog.csdn.net/augusdi/article/details/8808058
OpenMP程序设计的两个小技巧
https://blog.csdn.net/drzhouweiming/article/details/2472454

OpenMP本质上也是通过多线程来优化代码执行时间,因此无法与其他多线程方案,如pthread,NNPACK等同时使用。

Darknet with NNPACK
https://github.com/digitalbrain79/darknet-nnpack
NNPACK for Darknet
https://github.com/digitalbrain79/NNPACK-darknet
⚠️ **GitHub.com Fallback** ⚠️