YOLO:OpenMP评估 - zhonglong/TPV GitHub Wiki
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,对于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秒多。
根据OpenMP优化,可以推断出Darknet的性能瓶颈在矩阵乘法运算,具体来说是gemm.c文件的四个函数:
- gemm_nn
- gemm_tn
- gemm_nt
- gemm_tt
矩阵乘法更多说明,请参考:
darknet深度学习框架源码分析:详细中文注释,涵盖框架原理与实现语法分析 https://github.com/hgpvision/darknet
采用OpenMP就是来优化这四个函数的执行时间,受限于CPU核数,电源管理等因素,纯CPU方式进一步优化的空间有限。
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可以帮助开发人员快速实现并行代码,提升性能;但同时隐藏了实现细节,导致分析起来比较麻烦。 而且使用“#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