通過gcc或msvc,clang等編譯器編譯出來的C++源文件是.o文件。在windows上也就是PE文件,linux爲ELF文件,在這一步中,調用其它代碼文件中的函數的函數地址是未知的(00000),等到鏈接之後纔會替換掉函數地址的
C++是如何編譯的
C/C++編譯過程主要分爲4個過程
- 編譯預處理
- 編譯、優化階段
- 彙編過程
- 鏈接程序
編譯遊戲引擎的耗時
內網使用IB(incrediBuild)編譯引擎時總耗時2分23秒,編譯2分鐘,link耗時15秒
在vs中提高c++的編譯速度
達到修改一行代碼,10s內編譯完,link會花點時間,因爲所的工程都是lib,而不是dll,如果改成dll,則會更快。
調試信息的格式
把所有的工程的屬性這項: C/C++ - General - Debug Information Format ,改成:C7 compatible (/Z7)
實際上是在vcxproj文件中增加了這樣一項:<DebugInformationFormat>OldStyle</DebugInformationFormat>
Debug Information Format是一個編譯器選項,用於控制生成的調試信息的格式。
調試信息是一種用於調試程序的數據,包括變量名、函數名、行號等信息。在程序出現錯誤時,調試信息可以幫助開發人員快速定位問題。
Debug Information Format選項有以下幾種可選值:
-
None:不生成調試信息。
-
Program Database (/Zi):生成一個獨立的PDB文件,包含所有的調試信息。
-
Program Database for Edit and Continue (/ZI):生成一個獨立的PDB文件,包含所有的調試信息,並且支持編輯和繼續調試。
-
Old Style (/Z7):將調試信息嵌入到可執行文件中。
需要注意的是,生成調試信息會增加可執行文件的大小,因此在發佈版本時應該關閉調試信息生成。
另外,需要注意的是,如果使用了/DEBUG選項,那麼編譯器會自動將Debug Information Format選項設置爲Program Database (/Zi)。
預編譯頭
選中工程,右鍵 - 屬性 - C/C++ - Precompiled Headers - Precompiled Header 改成 Not
就是把一些固定的東西先編譯好,其他cpp文件直接引用就不copy了,這東西在分佈式下沒用, 單機是有效果的
什麼是預編譯頭?
includeN多的頭文件會導致編譯變慢,提取整個項目公共頭文件放到一起,只編譯一次,減少編譯時間。
增量編譯
在入口工程啓用增量編譯 : Linker - General - Enable Incremental Linking,勾選:Yes (/INCREMENTAL)
Linker - Optimization
右鍵 - 屬性,Linker - Optimization - 把這2項改成No
- References :No (/OPT:NOREF)
- Enable COMDAT Folding :No (/OPT:NOICE)
OptimizeReferences 用於控制是否優化未使用的函數和數據的代碼生成,當OptimizeReferences選項設置爲/OPT:REF時,編譯器將在鏈接時刪除未使用的函數和數據,以減小可執行文件的大小。這可以減少可執行文件的大小,提高程序的運行效率。
EnableCOMDATFolding 用於控制是否啓用COMDAT摺疊優化,當Enable COMDAT Folding選項設置爲Yes/OPT:ICF時,編譯器將啓用COMDAT摺疊優化。這可以減少可執行文件的大小,提高程序的運行效率。
Linker - Debugging
右鍵 - 屬性, Linker - Debugging , Generate Debug Info 改成Faster,可以link的更快
cgthreads(Code generation threads)
cl 默認使用的線程數是 4 ,最大可設置成 8 ,如果擁有更多核心時設置爲8將可以縮短構建時間,在開啓GL時效果更佳
在項目 配置屬性 > C/C++ > 命令行 增加 /cgthreads8
MP(Build with multiple processes)
當您編譯許多文件時,編譯器選項可以顯着減少構建時間。爲了縮短構建時間,編譯器會創建最多processMax自身的副本,然後同時使用這些副本來編譯源文件
其他模式建議開啓, published 模式 , 測試後構建時間並無明顯差異, 因爲MP對鏈接時編譯並不能起到提速作用
同樣也是在項目 配置屬性 > C/C++ > 命令行 增加 /mp
Incredibuild(集羣編譯)
使用ib編譯完之後,再從vs按F5即可啓動調試,已經生成了pdb文件。
非published模式(非WPO模式) 建議以下設置:
打開IB,切到Visual Studio Builds - Advanced
- 關閉 Limit Concurrent PDB file Instances to []
- 開啓 Force 64-bit tooset
使用集羣或者限制本機cpu的核數,這倆動態控制好,但是本機使用一半的核還是會卡,因爲其它進程不一定會分配到空閒的CPU
打開IB,切到Initiator - General
-
Avoid task execution on local machine when possible(儘可能避免在本地計算機上執行任務)
-
CPU Allocation : Limit maximum number of cores utilized in build to (限制構建過程中可使用的最大核心數爲)
WPO
全程序優化(Whole program optimization) 功能,是爲了增加文件之間的可見性,將編譯延遲到了鏈接時
WPO 可以提高程序的執行性能,一般在發佈模式下都會開啓此功能,代價只是增加了部分構建時長
如果開啓WPO模式後, 不建議使用 IB 構建, 也許可能會有未知問題
makefile
摘自UE引擎的某個makefile示例
CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Wcast-align -Wcast-qual -Wno-ctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-float-equal
CPPFLAGS += -I ../single_include -I . -I thirdparty/doctest -I thirdparty/fifo_map -DDOCTEST_CONFIG_SUPER_FAST_ASSERTS
SOURCES = src/unit.cpp \
src/unit-algorithms.cpp \
OBJECTS = $(SOURCES:.cpp=.o)
TESTCASES = $(patsubst src/unit-%.cpp,test-%,$(wildcard src/unit-*.cpp))
CMake
cmake.txt示例
cmake_minimum_required(VERSION 3.17)
project(mycpp)
set(CMAKE_CXX_STANDARD 11)
#添加需要編譯的文件
add_executable(strTest strTest.cpp)
C/C++爲什麼要寫頭文件?
來源: 爲什麼C/C++要分爲頭文件和源文件? - 知乎 (zhihu.com)
C時代的時候編譯器比較簡單,是固定的編譯和鏈接兩個過程,編譯一次只處理一個文件,進行預處理之後,頭文件會插入到這一個文件裏,不同源代碼文件的處理時獨立的,這樣如果頭文件裏面定義了一個函數的實現,編譯的時候所有引用這個頭文件的源碼文件,生成的obj裏都會有這個符號。而鏈接是通用的鏈接程序,從彙編時代就用的工具,沒有什麼高級功能,同一個符號鏈接時出現兩次是會報錯的。
但是,我們又說了,每個文件的編譯是獨立的,所以如果實現不在當前源文件裏面,調用的時候編譯器就不知道這個函數的類型和簽名,沒法生成調用代碼,所以必須在調用之前先聲明一遍。如果不把聲明寫在頭文件裏面,就必須在每個用到這個函數的源文件裏都聲明一遍,很不方便,所以綜合之後的解決方案就是實現寫源碼文件裏面,聲明寫頭文件裏面。