Compilers System
- Make (GNU make utility to maintain groups of programs)
- Ninja (a small build system with a focus on speed)
- 链接器
- CCache (a fast C/C++ compiler cache)
- 编译效率对比
- Mixing Clang with GCC
- Refer
Make (GNU make utility to maintain groups of programs)
The make
utility will determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them. The manual describes the GNU implementation of make, which was written by Richard Stallman and Roland McGrath, and is currently maintained by Paul Smith. Our examples show C programs, since they are very common, but you can use make with any programming language whose compiler can be run with a shell command. In fact, make is not limited to programs. You can use it to describe any task where some files must be updated automatically from others whenever the others change.
To prepare to use make, you must write a file called the makefile
that describes the relationships among files in your program, and the states the commands for updating each file. In a program, typically the executable file is updated from object files, which are in turn made by compiling source files.
Once a suitable makefile exists, each time you change some source files, this simple shell command: make
suffices to perform all necessary recompilations. The make program uses the makefile description and the last-modification times of the files to decide which of the files need to be updated. For each of those files, it issues the commands recorded in the makefile
.
make
executes commands in the makefile
to update one or more target names, where name is typically a program. If no -f
option is present, make will look for the makefiles GNUmakefile
, makefile
, and Makefile
, in that order.
Normally you should call your makefile either makefile or Makefile. (We recommend Makefile because it appears prominently near the beginning of a directory listing, right near other important files such as README.) The first name checked, GNUmakefile, is not recommended for most makefiles. You should use this name if you have a makefile that is specific to GNU make, and will not be understood by other versions of make. If makefile is ‘-‘, the standard input is read.
make
updates a target if it depends on prerequisite files that have been modified since the target was last modified, or if the target does not exist.
refer:
- https://man7.org/linux/man-pages/man1/make.1.html
- GNU make
- Automatic-Variables
Ninja (a small build system with a focus on speed)
Ninja is a small build system with a focus on speed. It differs from other build systems in two major respects: it is designed to have its input files generated by a higher-level build system, and it is designed to run builds as fast as possible.
对比使用ninja
和make
编译llvm-project-11.0.0.tar.xz :
结论:
ninja
在高并发时表现出色,因此对于编译性能要求很高的场景,建议使用ninja
构建系统。- 在上文的llvm-project对比测试中,同样由cmake生成编译脚本(
-G Ninja
,-G "Unix Makefiles"
)的情况下,ninja
表现明显优于Makefile
。
测试脚本:
#!/bin/bash
rm -rf build
mkdir -p build && cd build
export LLVM_INSTALL_DIR=$HOME/compile/test/install
COMPILE_MODE=gcc
#COMPILE_MODE=clang
## use `ccmake .` to use cmake gui
if [ $COMPILE_MODE == "gcc" ]; then
export CC=/opt/rh/devtoolset-7/root/usr/bin/cc
export CXX=/opt/rh/devtoolset-7/root/usr/bin/c++
## ninja
cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi;libunwind;lldb;compiler-rt;lld" -DCMAKE_INSTALL_PREFIX=$LLVM_INSTALL_DIR ../llvm-project-11.0.0/llvm
## makefile
#cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi;libunwind;lldb;compiler-rt;lld" -DCMAKE_INSTALL_PREFIX=$LLVM_INSTALL_DIR ../llvm-project-11.0.0/llvm
elif [ $COMPILE_MODE == "clang" ]; then
export CC=/root/compile/llvm_install/bin/clang
export CXX=/root/compile/llvm_install/bin/clang++
cmake -G "Ninja" -fuse-ld=lld -DCMAKE_USER_MAKE_RULES_OVERRIDE=./ClangOverrides.txt -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi;libunwind;lldb;compiler-rt;lld" -DCMAKE_INSTALL_PREFIX=$LLVM_INSTALL_DIR ../llvm-project-11.0.0/llvm
#cmake -G "Ninja" -fuse-ld=lld -DCMAKE_TOOLCHAIN_FILE=./LinuxToolchains.cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi;libunwind;lldb;compiler-rt;lld" -DCMAKE_INSTALL_PREFIX=$LLVM_INSTALL_DIR ../llvm-project-11.0.0/llvm
else
echo "error: $COMPILE_MODE invalid"
exit 1
fi
## 8 cpu, 16G mem
/usr/bin/time -f "real %e user %U sys %S" ninja -j8 -v
#/usr/bin/time -f "real %e user %U sys %S" ninja -j256
## LLD leaves its name and version number to a .comment section in an output
## readelf --string-dump .comment <output-file>
echo "have done"
ninja + gcc
top - 21:08:49 up 181 days, 16 min, 4 users, load average: 8.18, 8.43, 16.17
Tasks: 193 total, 10 running, 183 sleeping, 0 stopped, 0 zombie
%Cpu0 : 92.7 us, 7.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 96.7 us, 3.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 97.3 us, 2.7 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 96.7 us, 3.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu4 : 97.3 us, 2.7 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu5 : 90.3 us, 9.7 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu6 : 96.3 us, 3.7 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu7 : 92.7 us, 7.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 16165976 total, 7329180 free, 4121088 used, 4715708 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 11429800 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
19664 root 20 0 700252 652588 8600 R 99.7 4.0 0:07.02 cc1plus
19684 root 20 0 446644 397780 6464 R 99.7 2.5 0:03.94 cc1plus
19687 root 20 0 450332 402040 6400 R 99.7 2.5 0:03.84 cc1plus
19677 root 20 0 665556 615736 6476 R 99.3 3.8 0:05.49 cc1plus
19681 root 20 0 505488 457364 6452 R 98.7 2.8 0:04.45 cc1plus
19691 root 20 0 444044 394996 6404 R 97.0 2.4 0:03.17 cc1plus
19695 root 20 0 304324 254396 6028 R 47.2 1.6 0:01.42 cc1plus
19699 root 20 0 149372 102168 5920 R 20.3 0.6 0:00.61 cc1plus
results:
$time ninja -j8
real 40m26.115s
user 301m22.380s
sys 14m17.540s
增加ninja
并发可以增加速度,但是需要有较大的内存。以下为256并发时内存空间已不足,编译时会报内部错误。
[71/6710] Building CXX object lib/Support/CMakeFiles/LLVMSupport.dir/Debug.cpp.o
FAILED: lib/Support/CMakeFiles/LLVMSupport.dir/Debug.cpp.o
/opt/rh/devtoolset-7/root/usr/bin/c++ -DGTEST_HAS_RTTI=0 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Ilib/Support -I/root/compile/test/llvm-project-11.0.0/llvm/lib/Support -Iinclude -I/root/compile/test/llvm-project-11.0.0/llvm/include -fPIC -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-maybe-uninitialized -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wno-comment -fdiagnostics-color -ffunction-sections -fdata-sections -O3 -DNDEBUG -std=c++14 -fno-exceptions -fno-rtti -MD -MT lib/Support/CMakeFiles/LLVMSupport.dir/Debug.cpp.o -MF lib/Support/CMakeFiles/LLVMSupport.dir/Debug.cpp.o.d -o lib/Support/CMakeFiles/LLVMSupport.dir/Debug.cpp.o -c /root/compile/test/llvm-project-11.0.0/llvm/lib/Support/Debug.cpp
c++: internal compiler error: Killed (program cc1plus)
Please submit a full bug report,
with preprocessed source if appropriate.
See <http://bugzilla.redhat.com/bugzilla> for instructions.
top - 23:38:58 up 181 days, 2:46, 4 users, load average: 101.87, 24.81, 8.97
Tasks: 693 total, 105 running, 587 sleeping, 0 stopped, 1 zombie
%Cpu(s): 18.0 us, 17.8 sy, 0.0 ni, 0.2 id, 63.9 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 16165976 total, 157708 free, 15388396 used, 619872 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 183040 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
64 root 20 0 0 0 0 R 27.8 0.0 3:15.14 kswapd0
7 root rt 0 0 0 0 S 15.2 0.0 1:25.26 migration/0
22 root rt 0 0 0 0 S 14.8 0.0 1:20.00 migration/3
12958 root 20 0 0 0 0 Z 5.1 0.0 4:09.43 base_agent_net
15647 root 20 0 84008 35320 896 D 3.3 0.2 0:00.31 cc1plus
26692 root 20 0 286276 4072 0 D 3.0 0.0 72:34.73 sap1012
15430 root 20 0 109000 60828 2428 D 2.9 0.4 0:00.48 cc1plus
15158 root 20 0 166464 116372 1908 R 2.7 0.7 0:00.86 cc1plus
15470 root 20 0 99312 49680 356 R 2.6 0.3 0:00.41 cc1plus
15532 root 20 0 99356 49384 360 R 2.6 0.3 0:00.37 cc1plus
makefile + gcc
TODO
refer:
- Replacing Make with Ninja
- Ninja, a small build system with a focus on speed (homepage)
- The Performance of Open Source Software, Ninja
- Ninja, a new build system
链接器
ld (The GNU linker)
ld [options] objfile …
ld
combines a number of object and archive files, relocates their data and ties up symbol references. Usually the last step in compiling a program is to run ld
.
refer: ld(1) - Linux man page
ld.gold (an official GNU package)
The motivation for writing gold was to make a linker that is faster than the GNU linker, especially for large applications coded in C++.
# use ld.gold instead of ld for performance
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
# get link stage stats
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--stats")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stats")
lld (The LLVM Linker)
Replace ld
to lld
:
# orig: /opt/rh/devtoolset-7/root/etc/alternatives/ld -> /opt/rh/devtoolset-7/root/usr/bin/ld.bfd
# ln -s /opt/rh/devtoolset-7/root/usr/bin/ld.bfd /opt/rh/devtoolset-7/root/etc/alternatives/ld
#
rm /opt/rh/devtoolset-7/root/etc/alternatives/ld
ln -s /root/compile/llvm_install/bin/ld.lld /opt/rh/devtoolset-7/root/etc/alternatives/ld
ls -l /opt/rh/devtoolset-7/root/etc/alternatives/ld
lrwxrwxrwx 1 root root 37 Dec 13 16:47 /opt/rh/devtoolset-7/root/etc/alternatives/ld -> /root/compile/llvm_install/bin/ld.lld
LLD leaves its name and version number to a .comment
section in an output. If you are in doubt whether you are successfully using LLD or not, run readelf --string-dump .comment <output-file>
and examine the output. If the string “Linker: LLD” is included in the output, you are using LLD.
$readelf --string-dump .comment demo
String dump of section '.comment':
[ 0] Linker: LLD 12.0.0 (/root/compile/llvm-project/lld f76b7f22f085fbf9f2585923f7a3a0558d75964b)
[ 5d] clang version 12.0.0 (/root/compile/llvm-project/clang f76b7f22f085fbf9f2585923f7a3a0558d75964b)
[ be] GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-4)
[ ea] GCC: (GNU) 7.3.1 20180303 (Red Hat 7.3.1-5)
refer:
CCache (a fast C/C++ compiler cache)
ccache
is a compiler cache. It speeds up recompilation by caching the result of previous compilations and detecting when the same compilation is being done again. Supported languages are C, C++, Objective-C and Objective-C++.
ccache
has been carefully written to always produce exactly the same compiler output that you would get without the cache. The only way you should be able to tell that you are using ccache
is the speed. Currently known exceptions to this goal are listed under CAVEATS. If you ever discover an undocumented case where ccache changes the output of your compiler, please let us know.
There are two ways to use ccache. You can either prefix your compilation commands with ccache or you can let ccache masquerade as the compiler by creating a symbolic link (named as the compiler) to ccache. The first method is most convenient if you just want to try out ccache or wish to use it for some specific projects. The second method is most useful for when you wish to use ccache for all your compilations.
To use the first method, just make sure that ccache
is in your PATH
.
To use the symlinks method, do something like this:
cp ccache /usr/local/bin/
ln -s ccache /usr/local/bin/gcc
ln -s ccache /usr/local/bin/g++
ln -s ccache /usr/local/bin/cc
ln -s ccache /usr/local/bin/c++
And so forth. This will work as long as the directory with symlinks comes before the path to the compiler (which is usually in /usr/bin
). After installing you may wish to run “which gcc” to make sure that the correct link is being used.
- Features
- Keeps statistics on hits/misses.
- Automatic cache size management.
- Can cache compilations that generate warnings.
- Easy installation.
- Low overhead.
- Optionally compresses files in the cache to reduce disk space.
- Limitations
- Only knows how to cache the compilation of a single C/C++/Objective-C/Objective-C++ file. Other types of compilations (multi-file compilation, linking, etc) will silently fall back to running the real compiler.
- Only works with GCC and compilers that behave similar enough.
- Some compiler flags are not supported. If such a flag is detected, ccache will silently fall back to running the real compiler.
More: man ccache
$which gcc
/usr/lib64/ccache/gcc
$ll -lh `which gcc`
lrwxrwxrwx 1 root root 16 3月 5 2021 /usr/lib64/ccache/gcc -> ../../bin/ccache
$ll -lh /usr/bin/ccache
-rwxr-xr-x 1 root root 135K 2月 19 2020 /usr/bin/ccache
禁用 ccache
要禁用本地的 ccache,可以采用以下几种方法之一:
- 临时禁用 ccache:
在构建命令之前,将 CCACHE_DISABLE 环境变量设置为 1:
export CCACHE_DISABLE=1
然后运行构建命令(例如:make)。这将在当前会话中禁用 ccache。
- 永久禁用 ccache:
编辑 ~/.bashrc 或 ~/.bash_profile 文件,将以下行添加到文件末尾:
export CCACHE_DISABLE=1
保存文件并重新启动终端。这将在所有新的终端会话中禁用 ccache。
- 使用原始的编译器而不是 ccache 包装器
在构建命令中,直接指定原始编译器的路径,而不是使用 ccache 包装器。例如,如果您的原始 Clang 编译器位于 /usr/bin/clang,则可以在构建命令中使用此路径。
make CC=/usr/bin/clang
这将确保在构建过程中不使用 ccache。
- https://ccache.dev/
- ccache 4.8.2
- https://github.com/ccache/ccache/blob/master/README.md
- https://github.com/ccache/ccache/blob/master/doc/INSTALL.md
- ccache and clang, part 3
- How to use ccache selectively?
编译效率对比
在8核CPU,16G内存机器,对比gcc
, clang
, make
, ninja
, ld
, lld
不同组合情况下的编译效率。
- 使用
CMake
生成Unix Makefiles
,分别指定不同的gcc
或者clang
版本编译构建:(make.sh) - 使用
CMake
生成Ninja
,分别指定不同的gcc
或者clang
版本编译构建:(ninja.sh) - 对比不同链接器
ld
,lld
的性能差异
make.sh
#!/bin/bash
## https://stackoverflow.com/questions/7031126/switching-between-gcc-and-clang-llvm-using-cmake
rm -rf build
mkdir -p build && cd build
COMPILE_MODE=gcc
#COMPILE_MODE=clang
## use `ccmake .` to use cmake gui
if [ $COMPILE_MODE == "gcc" ]; then
export CC=/opt/rh/devtoolset-7/root/usr/bin/cc
export CXX=/opt/rh/devtoolset-7/root/usr/bin/c++
cmake -G "Unix Makefiles" -DCMAKE_USER_MAKE_RULES_OVERRIDE=./GccOverrides.txt ..
elif [ $COMPILE_MODE == "clang" ]; then
#export CC=/root/compile/llvm_install/bin/clang
#export CXX=/root/compile/llvm_install/bin/clang++
export CC=/usr/local/bin/clang
export CXX=/usr/local/bin/clang++
cmake -G "Unix Makefiles" -fuse-ld=lld -DCMAKE_USER_MAKE_RULES_OVERRIDE=./ClangOverrides.txt ..
#cmake -G "Unix Makefiles" -fuse-ld=lld -DCMAKE_TOOLCHAIN_FILE=./ClangToolchains.cmake ..
else
echo "error: $COMPILE_MODE invalid"
exit 1
fi
/usr/bin/time -f "real %e user %U sys %S" make -j8 VERBOSE=1
## LLD leaves its name and version number to a .comment section in an output
## readelf --string-dump .comment <output-file>
echo "have done"
ninja.sh
#!/bin/bash
## https://stackoverflow.com/questions/7031126/switching-between-gcc-and-clang-llvm-using-cmake
rm -rf build
mkdir -p build && cd build
COMPILE_MODE=gcc
#COMPILE_MODE=clang
## use `ccmake .` to use cmake gui
if [ $COMPILE_MODE == "gcc" ]; then
export CC=/opt/rh/devtoolset-7/root/usr/bin/cc
export CXX=/opt/rh/devtoolset-7/root/usr/bin/c++
cmake -G "Ninja" -DCMAKE_USER_MAKE_RULES_OVERRIDE=./GccOverrides.txt ..
elif [ $COMPILE_MODE == "clang" ]; then
#export CC=/root/compile/llvm_install/bin/clang
#export CXX=/root/compile/llvm_install/bin/clang++
export CC=/usr/local/bin/clang
export CXX=/usr/local/bin/clang++
cmake -G "Ninja" -fuse-ld=lld -DCMAKE_USER_MAKE_RULES_OVERRIDE=./ClangOverrides.txt ..
#cmake -G "Ninja" -fuse-ld=lld -DCMAKE_TOOLCHAIN_FILE=./ClangToolchains.cmake ..
else
echo "error: $COMPILE_MODE invalid"
exit 1
fi
/usr/bin/time -f "real %e user %U sys %S" ninja -j8 -v
## LLD leaves its name and version number to a .comment section in an output
## readelf --string-dump .comment <output-file>
echo "have done"
测试结果:
clang12 优于 gcc4.8/7/9,ninja 优于 make,lld 优于 ld。
Case | Time |
---|---|
gcc7 + make + ld | 25.7s |
clang12 + make + ld | 5.2s |
gcc7 + ninja + ld | 22s |
clang12 + ninja + ld | 4.7s |
gcc7 + make + lld | 17.8s |
clang12 + make + lld | 4.82s |
gcc7 + ninja + lld | 18.34s |
clang12 + ninja + lld | 4.15s |
gcc9 + make + lld | 10.03s |
gcc9 + ninja + lld | 7.90s |
gcc4.8 + make + lld | 8.93s |
gcc4.8 + ninja + lld | 8.30s |
Mixing Clang with GCC
Can Clang compile code with GCC compiled .a libs?
I have my project currently compiling under gcc
. It uses Boost, ZeroMQ as static .a
libraries and some .so
libraries like SDL. I want to go clang
all the way but not right now. I wonder if it is possible to compile code that uses .a
and .so
libraries that were compiled under gcc with clang?
Answers:
Yes, you usually can use clang
with GCC
compiled libraries (and vice versa, use gcc
with CLANG
compiled libraries), because in fact it is not compilation(编译) but linking(链接) which is relevant. You might be unlucky and get unpleasant suprises.
You could in principle have some dependencies on the version of libstdc++
used to link the relevant libraries (if they are coded in C++). Actually, that usually does not matter much.
In C++, name mangling might in theory be an issue (there might be some corner cases, even incompatibilities between two different versions of g++
). Again, in practice it is usually not an issue.
So usually you can mix CLANG
(even different but close versions of it) with GCC
but you may have unpleasant surprises. What should be expected from any C++ compiler (be it CLANG
or GCC
) is just to be able to compile and link an entire software (and all libraries) together using the same compiler and version (and that includes the same C++ standard library implementation). This is why upgrading a compiler in a distribution is a lot of work: the distribution makers have to ensure that all the packages compile well (and they do get surprises!).
Beware that the version of libstdc++
does matter. Both Clang
& GCC
communities work hard to make its ABI compatible for compiler upgrades, but there are subtle corner cases. Read the documentation of your particular and specific C++ standard library implementation. These corner cases could explain mysterious crashes when using a good C++ library binary (compiled with GCC 5
) in your code compiled with GCC 8
. The bug is not in the library, but the ABI evolved incompatibly.
Another answers:
At least for Crypto++ library this does not work (verified :-( ). So for c++ code it is less likely to work, while pure c code would probably link OK.
The solution appears to be: if you need to compile C++ code with clang, and link it to a gcc-compiled library, use clang++ -stdlib=libstdc++
. The linking is successful, and the resulting binary runs correctly.
CAVEAT(注意): It does not seem to work the other way: even though you can build a library compiled with “clang++ -stdlib=libstdc++” and link gcc-compiled code with it, this code will crash with SEGV. So far I found the only way to link with a clang-compiled library is compiling your code with clang, not gcc.
gcc vs clang common library issue
I have two applications, one compiled with gcc(c++) and another compiled with clang++. I am to use common shared boost library for both the applications. My question is whether to compile boost shared library using clang compiler or gcc compiler. Can I use boost library compiled with gcc in my application that is being compiled using clang?
Answers:
g++
and clang++
are compatible as compilers (because they both follow the Itanium ABI
), but they may come with incompatible standard library implementations.
g++
comes with a standard library implementation called libstdc++
. You can direct g++ to use a different implementation but this is not exactly trivial.
clang++
sometimes comes without a standard library implementation of its own (and is configured to use implementation provided by g++
), and sometimes comes with an implementation called libc++
. One can easily switch clang++
to use either libc++
or libstdc++
with a single command line option.
So your question boils down to what standard library implementation(s) your applications use. If they use the same implementation, you need to build Boost with that implementation (and either compiler). If they use different implementations, you need two separate builds of Boost.
Mixing components built against different standard library implementations in the same application can sometimes be done, but is not straightforward, entails a lot of restrictions, and with things like boost is either not feasible or downright impossible.