前段时间我将YOLOv5部署在SS928板端的详细过程公开了出来,现在回过头发现自己只是会用这个仓库,却没有深入关注内部的代码到底是怎么运行的。为了更加深入地了解YOLO是如何跑在SS928上的,我将深入代码内部,从代码功能到代码实现方式细致地介绍。
这是我的仓库目录:
在这里插入图片描述
在每次修改完代码需要编译时,运行build.sh即可。
这是文件的内容:

####!/bin/sh

mkdir -p build/
pushd build/
cmake ..
make -j8
popd

这个文件的功能是创建并进入build/目录,指定根目录下的CMakeLists.txt为构建脚本,检查系统环境,生成构建系统文件,缓存配置将配置结果保存build/CMakeCache.txt,调用生成的Makefile启动多线程编译最后回到项目根目录。
CMakeLists.txt 是 ​CMake​ 构建系统的核心配置文件,用于定义如何编译、链接和安装我们的项目。
CMakelist.txt全部文件内容参考仓库,这里解释一下其中的内容:

//指定CMake的最低版本为3.5,如果低于此版本则会报错并且终止构建
cmake_minimum_required(VERSION 3.5) 

//定义项目名称,创建变量:${PROJECT_NAME}、${CMAKE_PROJECT_NAME}:ss928_yolov5,${PROJECT_SOURCE_DIR}:项目根目录,${PROJECT_BINARY_DIR}:构建目录路径。
PROJECT(ss928_yolov5)

//加载外部 CMake 脚本文件(.cmake),将其内容插入当前文件
include(${CMAKE_SOURCE_DIR}/cmake/ss928.cmake)
...

//设置 可执行文件​(如 ss928_yolov5)和运行时文件(如 .dll、.so)的输出目录为${CMAKE_SOURCE_DIR}/output/。
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY  "${CMAKE_SOURCE_DIR}/output/")

//添加 ​头文件搜索路径,编译器会在这些路径中查找 #include 的头文件
INCLUDE_DIRECTORIES(...)

//添加 ​库文件搜索路径,链接器会在这些路径中查找 .a(静态库)或 .so(动态库)
LINK_DIRECTORIES(...)

//自动收集指定目录下的源代码文件​(.c, .cpp, .cc 等),并将文件列表存入一个变量(如SOCSDKCOMMON_SRCLIST)
aux_source_directory(${CMAKE_SOURCE_DIR}/ss928sdk/common/ SOCSDKCOMMON_SRCLIST)

//定义可执行文件的构建目标,并指定其依赖的源文件。
add_executable(${PROJECT_NAME} ${SVP_SRCLIST} ${SOCSDKCOMMON_SRCLIST})

//​为可执行文件或库指定链接的依赖库​(静态库 .a 或动态库 .so)
target_link_libraries(${PROJECT_NAME} ${SOC_LIBS} ${SYSTEM_LINK_LIB} libncnn.a)

//定义自定义命令,在构建过程中执行特定操作(如文件复制、脚本运行)
//cmake运行过程分为两个部分,配置部分和构建部分。在cmakelist中,这段命令只是在配置的时候定义,而在构建的时候执行,将命令转化为make格式的命令
add_custom_target(...)

//自定义规则`cpfiles`,ALL表示默认包含在make构建目标中,依赖于{PROJECT_NAMEPROJECT_NAME}目标
add_custom_target(cpfiles ALL DEPENDS  ${PROJECT_NAME})

总结来说,这个cmakelist.txt文件可以调用不同的生成器生成构建文件。相比于我们自己写构建文件(Makefile等),cmake可以指定生成器决定构建文件的输出格式。而不同的生成器生成的构建文件格式不同在哪里呢?
这里介绍四种不同的生成器:Unix Makefiles、ninja、visual studio、Xcode。在构建的时候,每一个生成器相同点是都会创建一个cmakefiles文件夹,其中包含cmakecache.txt文件用于存储临时文件和中间数据,cmakeoutput.log/CmakeError.log等日志记录编译时出现的命令与报错。
而对于不同的生成器会生成各自特殊的构建文件:Unix Makefiles会生成一个传统的makefile文件,ninja会生成一个build.ninja文件,visul studio会生成.sln(解决方案文件)和.vcxproj(项目文件),xcode会生成.xcodeproj目录。对于visual studio和xcode,一个基于在windows下生成编译文件,一个专用于macOS,我们在这里不做太深入的目录结构解析。
我们在这里详细解析常常用在Linux下的两个生成器:Unix Makefiles和ninja。Unix Makefiles会生成一个Makefile,在编译的时候会直接调用预装的make构建工具,并且支持条件判断(ifeq)、循环(foreach)函数定义等高级功能,也可以在makefile中编写shell脚本支持复杂的构建逻辑。开发者可以手动修改makefile中的内容做到定制化编译。而ninja生成的ninja文件是直接由cmake构建的,需要提前安装ninja的工具,但相比于makefile来说,ninja的构建文件高度精简,只包含必要的构建规则而没有冗余的逻辑。
我们可以来对比一下这两个工具的编译区别。修改我们build.sh中的代码,让cmake指定编译生成器:

#!/bin/sh
#!/bin/sh

# 创建 build 目录
mkdir -p build/

# 进入 build 目录
pushd build/ > /dev/null

# 清理旧的构建文件(可选)
rm -rf *

# 测试 Unix Makefiles
echo "===== Testing Unix Makefiles ====="
cmake -G "Unix Makefiles" ..
time make -j8

# 清理构建文件
rm -rf *

# 测试 Ninja
echo "===== Testing Ninja ====="
cmake -G "Ninja" ..
time ninja -j8

# 返回原目录
popd > /dev/null

第一次编译发现使用时间使用Unix MakeFiles比ninja更快?
Unix Makefiles:

ninja:
在这里插入图片描述
而第二次编译时情况又相反:
Unix Makefiles:
在这里插入图片描述
ninja:
在这里插入图片描述
经过多次修改和编译后,makefile编译的时间总会比ninja时间略长。
makefile:

ninja:
在这里插入图片描述
在小型项目中无法深入体现不同生成器生成构建时在时间层面上的不同(因为都不到一秒就构建完成了),不过从数据中我们能看出ninja在构建速度这一方面的优势,而在更加大型的项目中ninja也许会展现出更加明显的速度优势。

更多推荐