人工智能的Linux下cpp部署

669 阅读6分钟

Linux下部署cpp文件的方式

  • 作者是一个深度学习工作者,以前常用的是python做算法开发。最近要将算法部署到板子上,所以考虑学习C++作为部署的语言。目前主要是记录自己的学习过程。后续还会更新TensorRT、onnx模型等相关知识。
  • 此篇参考于B站的 www.bilibili.com/video/BV1fy… 这位老师讲的是真的好,大家可以对应着学习一下。

gcc

安装

1、  安装gcc、g++、GDB

sudo apt install build-essential gdb

2、  安装Cmake

# 通过以下命令安装编译器和调试器
sudo apt install cmake
# 若安装成功
cmake --version

编译过程

1、  预处理

#include命令告诉预处理器读取系统文件iostream的内容,并把它直接插入到程序文本当中。生成以.i结尾的文件格式

# -E 选项指示编译器仅对输入文件进行预处理
g++ -E textdemo01.cpp -o testdemo01.i

2、  编译

生成汇编语言程序

# -S 编译选项告诉 g++ 在为 C++ 代码产生了汇编语言文件后停止编译
# g++ 产生的汇编语言文件的缺省扩展名是 .s 。
g++ -S testdemo01.i -o testdemo01.s

3、  汇编

汇编器会翻译成机器语言指令,并将结果保存到目标二进制文件中,它的字节编码是机器语言指令而不是字符。

# -c 选项告诉 g++ 仅把源代码编译为机器语言的目标代码
# 缺省时 g++ 建立的目标代码文件有一个 .o 的扩展名。
g++ -C testdemo01.s -o testdemo01.o

4、  链接

链接器将各种.o文件进行合并并生成可执行目标文件,可以被加载到内存中,由系统进行执行。连接目标代码,生成可执行程序。

# -o 编译选项来为将产生的可执行文件用指定的文件名
g++ testdemo01.o -o testdemo01

ps:按步骤的时候可能会出现链接不通过的情况。报错信息如下:(还不知道为啥)

/usr/bin/ld: test.o: _ZSt4cout: invalid version 3 (max 0)
/usr/bin/ld: test.o: error adding symbols: bad value
collect2: error: ld returned 1 exit status

重要编译参数

-g:编译带有调试信息的可执行文件

g++ -g test.cpp

-O[n]:对代码进行优化。

所谓优化,例如省略掉代码中从未使用过的变量、直接将常量表达式用结果值代替等等,这些操作会缩减目标文件所包含的代码量,提高最终生成的可执行文件的运行效率。

# -O 选项告诉 g++ 对源代码进行基本优化。这些优化在大多数情况下都会使程序执行的更快。 -O2 选项告诉 g++ 产生尽可能小和尽可能快的代码。 如-O2,-O3,-On(n 常为0–3)
# -O 同时减小代码的长度和执行时间,其效果等价于-O1
# -O0 表示不做优化
# -O1 为默认优化
# -O2 除了完成-O1的优化之外,还进行一些额外的调整工作,如指令调整等。
# -O3 则包括循环展开和其他一些与处理特性相关的优化工作。
# 选项将使编译的速度比使用 -O 时慢, 但通常产生的代码执行速度会更快。
# 使用 -O2优化源代码,并输出可执行文件
g++ -O2 test.cpp

-l和-L的使用

● -l:指定需要链接库的名字。

# 链接glog库
g++ -lglog test.cpp

● -L:指定库文件所在的目录。

# -L/usr/local/lib

综合例子:

# -lopencv_core和-lopencv_highgui指定需要链接的OpenCV库。
# -L/usr/local/lib指定OpenCV库的路径。
g++ -o main main.cpp -lopencv_core -lopencv_highgui -L/usr/local/lib

-I(i的大写)

用于指定头文件的路径

g++ -o main main.cpp -I/usr/local/include/opencv4 -I/usr/include/jsoncpp

-Wall

打印警告信息

# 打印出gcc提供的警告信息
g++ -Wall test.cpp

-w

关闭警告信息

# 关闭所有警告信息
g++ -w test.cpp

-std=c++11

设置编译标准

# 使用c++1 标准编译 test.cpp
g++ -std=c++11 test.cpp

-o

指定输出文件名

g++ test.cpp -o test

-D

定义宏。

-DDEBUG 定义DEBUG宏,可能文件中有DEBUG宏部分的相关信息,用个DDEBUG来选择开启或关闭

#include<iostream>
using namespace std;

int main(){
    #ifdef DEBUG
        cout << "debug" << endl;
    #endif
        cout << "not" << endl;
}
# 对上述代码编译,会输出#ifdef DEBUG中的内容
g++ -DDEBUG test1.cpp -o test1

两种编译(静态库/动态库)

链接静态库

# 汇编,生成Swap.o文件
g++ Swap.cpp -c -I../include

# 使用GNU ar命令创建一个静态库
# 其中s选项告诉ar创建一个新的静态库,r选项告诉ar替换库中已经存在的同名文件,而不是将其添加到库中。
ar rs libSwap.a Swap.o

# 链接,生成可执行文件:staticmain。
# 其中src中的静态库为libSwap.a
g++ main.cpp -Iinclude -Lsrc -lSwap -o staticmain

链接动态库

# -fPIC:编译成位置无关的代码,因为:动态库需要在程序运行时进行动态加载
# -shared:生成的是一个动态链接库。
g++ swap.cpp -I../include -fPIC -shared -o libSwap.so

# 链接,生成可执行文件:sharemain
# 这里链接器会默认链接动态库。
# 如果想链接静态库,您可以使用参数“-static”
g++ main.cpp -Iinclude -Lsrc -lSwap -o sharemain

ps:当使用-l参数时,会告诉编译器链接一个名为libxxx.a或libxxx.so的库文件,其中xxx为库的名称。在这个例子中,-lSwap实际上告诉编译器链接libSwap.a静态库文件。

辨认可执行文件动态库还是静态库

在Linux系统中,可以使用命令ldd来显示一个可执行文件所依赖的动态库。

动态\静态区别

● 静态库

○ 编译链接期间复制到可执行文件

○ 仅仅需要拷贝可执行文件即可执行

○ 修改需要重新编译可执行文件

● 动态库

○ 编译链接期间仅仅做了引用,仅在运行时才被动态加载到内存

○ 需要拷贝动态库文件

○ 修改仅仅需要更新动态库

执行

● 运行静态链接的可执行文件

./staticmain

● 运行动态链接的可执行文件

在Linux系统中,可执行文件在运行时需要依赖一些动态链接库,这些库可能位于操作系统的默认搜索路径中,或者位于用户指定的路径中。LD_LIBRARY_PATH 环境变量被用来指定动态链接库的搜索路径,当可执行文件需要使用某个动态链接库时,系统会在 LD_LIBRARY_PATH 中指定的路径中查找该库。

# 运行当前目录下名为 sharemain 的可执行文件,并在当前目录下的 src 目录中查找可执行文件所需要的动态链接库。
LD_LIBRARY_PATH=src ./sharemain

Cmake

重要指令

set

显示的定义变量

# 定义一个SRC,包含两个源文件:sayhello.cpp和hello.cpp
set(SRC sayhello.cpp hello.cpp)

# 当需要使用的时候
add_executable(hello ${SRC})

include_directories

用于向编译器添加头文件目录

include_directories(include)

link_directories

用于添加多个特定的库文件搜索路径

# 将/usr/lib/mylibfolder 和 ./lib 添加到库文件搜索路径
link_directories(/usr/lib/mylibfolder ./lib)

add_library

生成库文件

# 通过变量 SRC 生成 libhello.so 动态库
add_library(hello SHARED ${SRC})

# 生成静态库
add_library(hello STATIC ${SRC})

add_compile_options

添加编译参数

#  添加编译参数 -Wall -std=c++11 -O2
add_compile_options(-Wall -std=c++11 -O2)

add_executable

生成可执行文件

add_executable(hello ${SRC})

target_link_libraries

为可执行文件中链接共享库

# 将hello动态库文件链接到可执行文件main
target_link_libraries(main hello)

# 静态库
target_link_libraries(main hello.a)

add_subdirectory

一般用于将第三方库添加至本项目。

下面是一个添加第三方库的例子

# my_project/CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

project(my_project)

# 将 lib 目录添加到项目中
add_subdirectory(third_party/third_lib)

# 添加项目的可执行文件
add_executable(main main.cpp)

# 链接 my_executable 和 third_lib
target_link_libraries(main third_lib)
# my_project/third_party/third_lib/CMakeLists.txt

# 添加 third_lib 的源代码文件
add_library(third_lib third_lib.cpp)

# 在 third_lib 构建目标中添加编译选项和链接选项
target_compile_options(third_lib PRIVATE -O3)
target_link_libraries(third_lib PRIVATE some_other_library)

aux_source_directory

它的作用是将指定目录中的源代码文件列表保存到一个变量中

# 定义SRC变量,其值为当前目录下所有的源代码文件
aux_source_directory(src SRC)
# 编译SRC变量所代表的源代码文件,生成main可执行文件
add_executable(main ${SRC})

Cmake部署

mkdir build
cd build
cmake ..
make