Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【CMake 系列】(十)编译速度以及程序性能优化相关 #145

Open
xizhibei opened this issue Jul 29, 2020 · 0 comments
Open

Comments

@xizhibei
Copy link
Owner

写 C/C++ 的同学都知道,项目稍大点,编译速度就开始拖后腿了,这对于我们来说是个又爱又恨的时候:急着改代码的时候,慢能消耗我们的耐心,能将我们逼疯,而我们想暂时休息会儿的时候,却可以借此去散步喝茶了。

另外,程序运行时的速度,又是另外一个关键的速度了,编译速度慢我们可以忍,但是运行速度慢可忍不了,就算我们忍得了,领导或者用户也是无法忍的。

话说回来,为了我们自己的开发效率,提升编译速度是无可非议的,今天我们就来说说,如何在 CMake 中优化编译以及以及程序本身。

编译速度优化

Ninja Generator

CMake 的默认 Generator 是 Unix Makefiles,也就是最常见的 make 命令,但是另一个 Generator Ninja 却是更好的选择,如果你没有用过,建议试试。

CCache

最简单,也是效果最好的,就是开启编译缓存,ccache 便是我们需要的工具。

它的原理也很简单,就是包装编译器,接收编译参数、文件,当检测到没有对应缓存的时候,调用编译器,将生成物缓存到文件中去,下次如果编译参数以及文件没有变化,就能够直接从缓存文件中提取,这样,就可以大大减少重复编译时候的时间。

在 CMake 早期版本中 (2.8 Unix Makefiles 以及 3.4 Ninja 之前的版本),没有 ccache 的支持,我们需要手动设置:CMAKE_C_COMPILER 以及 CMAKE_CXX_COMPILER,将 ccache 作为前缀即可:

set(CMAKE_C_COMPILER ccache gcc)
set(CMAKE_CXX_COMPILER ccache g++)

而另外较新版本中,就更容易了:

find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
  message(STATUS "Set up ccache ...")
  set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
  set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif()

然后,XCode generator 可参考 Using ccache with CMake

或许你会怀疑它究竟有多少效果,下面的结果是我在一台机器上运行 ccache -s 的统计结果:

cache directory                     /home/me/.ccache
primary config                      /home/me/.ccache/ccache.conf
secondary config      (readonly)    /etc/ccache.conf
stats zero time                     Tue Apr  7 16:06:27 2020
cache hit (direct)                 41056
cache hit (preprocessed)            7179
cache miss                         25047
cache hit rate                     65.82 %
called for link                    10928
called for preprocessing            5929
compile failed                      3055
preprocessor error                  1325
can't use precompiled header          86
bad compiler arguments                56
autoconf compile/link               3428
no input file                        616
cleanups performed                    90
files in cache                     25512
cache size                           4.5 GB
max cache size                       5.0 GB

可以这么说,它在过去几个月的开发过程中,帮我节约了大约 65.82% 的编译时间。

Precompiled headers (PCH) 以及 Unity builds2

Precompiled headers:也就是预编译头,可以大大将少 C++ 头文件的重复编译时间,你可以将一些第三方库,比如 nlohmann/json 、spdlog/spdlog.h、Boost 以及 项目中很少变动的 C++ 头文件加到预编译中:

target_precompile_headers(<my_target> PRIVATE my_pch.h)

但是,生成的中间文件,会非常大,占用比较大的磁盘空间。

Unity builds:也可以按照字面意义上去理解,即一体化编译,将多个 CPP 文件合并到一起进行编译,这样的话:编译器可以解析更少的次数、相同模版的优化、更少编译器调用次数、链接器也会更友好。

使用也很简单:

set(CMAKE_UNITY_BUILD ON)
set_target_properties(<target> PROPERTIES UNITY_BUILD ON)

然而,这两个算是高级招数,所以不是所有的项目都适合用,没准用了之后会增加项目的维护成本,如果不知道怎么用,很可能你用无法编译成功。

具体的使用,也会挺复杂,有不少的坑,如果各位有兴趣,下次单独讲讲。

其它

  • gcc 换成 clang;
  • 静态链接换成动态链接;
  • 换台高性能的机器,换个更好的 CPU 以及 SSD 磁盘,甚至用上内存磁盘(这种算是用钱换性能了,但是效果还是非常显著的);

程序性能优化

对于 CMake 来说,最简单优化的莫过于将 Debug 改为 Release 模式。

另外,就是 Interprocedural optimization,你可以理解为程序级别的 Release 模式,因为普通的 Release 模式是单个文件级别的。

当然,不是每个编译器都支持,你需要先检查:

include(CheckIPOSupported)
check_ipo_supported(RESULT _IsIPOSupported)
  if(_IsIPOSupported)
  message(STATUS "Turn on INTERPROCEDURAL_OPTIMIZATION")
  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()

其实在性能优化上面,编译器能做的比较有限,更多的还是在于编码阶段,对整个程序的优化。

而业务逻辑上面,算法上面,等待,都不是编译器能解决的问题,却是能最终影响结果的。

Ref

  1. Using ccache with CMake
  2. CMake 3.16 added support for precompiled headers & unity builds - what you need to know
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant