Debug模式和Release模式有什么區(qū)別你知道嗎?
本文轉(zhuǎn)載自微信公眾號(hào)「程序喵大人」,作者程序喵大人 。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序喵大人公眾號(hào)。
張愛(ài)玲曾說(shuō)過(guò):“開(kāi)發(fā)過(guò)程就是個(gè)玄學(xué)。”
有時(shí)候,程序在Debug模式下運(yùn)行的好好的,一Release就crash了。
有時(shí)候,程序在Debug模式下崩潰了,Release模式下居然能正常運(yùn)行。
可能很多人都遇到過(guò)以上的情況。
用C/C++的朋友都知道編譯器編譯有各種優(yōu)化級(jí)別,編譯器優(yōu)化級(jí)別大體如下:
O0(默認(rèn)選項(xiàng)):不開(kāi)啟優(yōu)化,方便功能調(diào)試
Og:方便調(diào)試的優(yōu)化選項(xiàng)(比O1更保守)
O1:保守的優(yōu)化選項(xiàng),打開(kāi)了四十多個(gè)優(yōu)化選項(xiàng)
O2:常用的發(fā)布優(yōu)化選項(xiàng),在O1的基礎(chǔ)上額外打開(kāi)了四十多個(gè)優(yōu)化選項(xiàng),包括自動(dòng)內(nèi)聯(lián)等規(guī)則
Os:產(chǎn)生較小代碼體積的優(yōu)化選項(xiàng)(比O2更保守)
O3:較為激進(jìn)的優(yōu)化選項(xiàng)(對(duì)錯(cuò)誤編碼容忍度最低),在O2的基礎(chǔ)上額外打開(kāi)了十多個(gè)優(yōu)化選項(xiàng)
Ofast:打開(kāi)可導(dǎo)致不符合IEEE浮點(diǎn)數(shù)等標(biāo)準(zhǔn)的性能優(yōu)化選項(xiàng)。
具體介紹如下:
O0:編譯器默認(rèn)就是O0,該選項(xiàng)下不會(huì)開(kāi)啟優(yōu)化,方便開(kāi)發(fā)者調(diào)試。
O1:致力于在不需要過(guò)多的編譯時(shí)間情況下,盡量減少代碼大小和盡量提高程序運(yùn)行速度,它開(kāi)啟了下面的優(yōu)化標(biāo)志:
- -fdelayed-branch
- -fdse
- -fforward-propagate
- -fguess-branch-probability
- -fif-conversion
- -fif-conversion2
- -finline-functions-called-once
- -fipa-modref
- -fipa-profile
- -fipa-pure-const
- -fipa-reference
- -fipa-reference-addressable
- -fmerge-constants
- -fmove-loop-invariants
- -fomit-frame-pointer
- -freorder-blocks
- -fshrink-wrap
- -fshrink-wrap-separate
- -fsplit-wide-types
- -fssa-backprop
- -fssa-phiopt
- -ftree-bit-ccp
- -ftree-ccp
- -ftree-ch
- -ftree-coalesce-vars
- -ftree-copy-prop
- -ftree-dce
- ftree-dominator-opts
- -ftree-dse
- -ftree-forwprop
- -ftree-fre
- -ftree-phiprop
- -ftree-pta
- -ftree-scev-cprop
- -ftree-sink
- -ftree-slsr
- -ftree-sra
- -ftree-ter
- -funit-at-a-time
Og:如果是為了調(diào)試,該選項(xiàng)是比O0更好的選擇,它會(huì)打開(kāi)O1大部分優(yōu)化標(biāo)志,但是不會(huì)啟用那些影響調(diào)試的標(biāo)志:
- -fbranch-count-reg
- -fdelayed-branch
- -fdse
- -fif-conversion
- -fif-conversion2
- -finline-functions-called-once
- -fmove-loop-invariants
- -fssa-phiopt
- -ftree-bit-ccp
- -ftree-dse
- -ftree-pta
- -ftree-sra
O2:常見(jiàn)的Release級(jí)別,該選項(xiàng)下幾乎執(zhí)行了所有支持的優(yōu)化選項(xiàng),它增加了編譯時(shí)間,提高了程序的運(yùn)行速度,又額外打開(kāi)了以下優(yōu)化標(biāo)志:
- -flra-remat
- -foptimize-sibling-calls
- -foptimize-strlen
- -fpartial-inlining
- -fpeephole2
- -freorder-blocks-algorithm=stc
- -freorder-blocks-and-partition
- -freorder-functions
- -frerun-cse-after-loop
- -fschedule-insns
- -fschedule-insns2 -fsched-interblock
- -fsched-spec
- -fstore-merging
- -fstrict-aliasing
- -fthread-jumps
- -ftree-builtin-call-dce
- -ftree-pre
- -ftree-switch-conversion
- -ftree-tail-merge
- -ftree-vrp
Os:打開(kāi)了幾乎所有的O2優(yōu)化標(biāo)志,除了那些經(jīng)常會(huì)增加代碼大小的優(yōu)化標(biāo)志:
- -falign-functions
- -falign-jumps
- -falign-labels
- -falign-loops
- -fprefetch-loop-arrays
- -freorder-blocks-algorithm=stc
它還啟用了-finline-functions優(yōu)化標(biāo)志,使編譯器根據(jù)代碼大小而不是程序運(yùn)行速度進(jìn)行優(yōu)化,為了減少代碼大小。
O3:在O2的基礎(chǔ)上又打開(kāi)了以下優(yōu)化標(biāo)志
- -fgcse-after-reload
- -fipa-cp-clone
- -floop-interchange
- -floop-unroll-and-jam
- -fpeel-loops
- -fpredictive-commoning
- -fsplit-loops
- -fsplit-paths
- -ftree-loop-distribution
- -ftree-loop-vectorize
- -ftree-partial-pre
- -ftree-slp-vectorize
- -funswitch-loops
- -fvect-cost-model
- -fvect-cost-model=dynamic
- -fversion-loops-for-strides
Ofast:更加激進(jìn)的編譯選項(xiàng),它不會(huì)嚴(yán)格遵循標(biāo)準(zhǔn),在O3的優(yōu)化基礎(chǔ)上,它又開(kāi)啟了一些可能導(dǎo)致不符合IEEE浮點(diǎn)數(shù)等標(biāo)準(zhǔn)的性能優(yōu)化選項(xiàng),如- fast-math, -fallow-store-data-races等。
tips:上述優(yōu)化選項(xiàng)如果想要了解具體含義可以看https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html 官方文檔。
編譯器有這么多優(yōu)化級(jí)別,Debug版本和Release版本其實(shí)就是優(yōu)化級(jí)別的區(qū)別,Debug稱為調(diào)試版本,編譯的結(jié)果通常包含有調(diào)試信息,沒(méi)有做任何優(yōu)化,方便開(kāi)發(fā)人員進(jìn)行調(diào)試,Release稱為發(fā)布版本,不會(huì)攜帶調(diào)試信息,同時(shí)編譯器對(duì)代碼進(jìn)行了很多優(yōu)化,使代碼更小,速度更快,發(fā)布給用戶使用,給用戶使用以更好的體驗(yàn)。但Release模式編譯比Debug模式花的時(shí)間也會(huì)更多。
Debug模式下在內(nèi)存分配上有所區(qū)別,在我們申請(qǐng)內(nèi)存時(shí),Debug模式會(huì)多申請(qǐng)一部分空間,分布在內(nèi)存塊的前后,用于存放調(diào)試信息。
對(duì)于未初始化的變量,Debug模式下會(huì)默認(rèn)對(duì)其進(jìn)行初始化,而Release模式則不會(huì),所以就有個(gè)常見(jiàn)的問(wèn)題,局部變量未初始化時(shí),Debug模式和Release模式表現(xiàn)有所不同。
- bool func() {
- bool found;
- for (int i = 0; i < vec.size(); ++i) {
- if (vec[i] == 3) {
- found = true;
- }
- }
- return found;
- }
Debug模式下可能運(yùn)行正常,但Release模式下可能會(huì)返回錯(cuò)誤結(jié)果,因?yàn)閒ound局部變量在Release模式下沒(méi)有初始化。
Debug模式以32字節(jié)為單位分配內(nèi)存,例如當(dāng)申請(qǐng)24字節(jié)內(nèi)存時(shí),Release模式下是正常的分配24字節(jié),Debug模式會(huì)分配32字節(jié),多了8字節(jié),所以有些數(shù)組越界問(wèn)題在Debug模式下可以安全運(yùn)行,Release模式下就會(huì)出問(wèn)題。
Debug模式下可以使用assert,運(yùn)行過(guò)程中有異?,F(xiàn)象會(huì)及時(shí)crash,Release模式下模式下不會(huì)編譯assert,遇到不期望的情況不會(huì)及時(shí)crash,稀里糊涂繼續(xù)運(yùn)行,到后期可能會(huì)產(chǎn)生奇奇怪怪的錯(cuò)誤,不易調(diào)試,殊不知其實(shí)在很早之前就出現(xiàn)了問(wèn)題。編譯器在Debug模式下定義_DEBUG宏,Release模式下定義NDEBUG宏,預(yù)處理器就是根據(jù)對(duì)應(yīng)宏來(lái)判斷是否開(kāi)啟assert的。
數(shù)據(jù)溢出問(wèn)題,在一個(gè)函數(shù)中,存在某些從未被使用的變量,且函數(shù)內(nèi)存在數(shù)據(jù)溢出問(wèn)題,在Debug模式下可能不會(huì)產(chǎn)生問(wèn)題,因?yàn)椴粫?huì)對(duì)該變量進(jìn)行優(yōu)化,它在??臻g中還是占有幾個(gè)字節(jié),但是Release模式下可能會(huì)出問(wèn)題,Release模式下可能會(huì)優(yōu)化掉此變量,??臻g相應(yīng)變小,數(shù)據(jù)溢出就會(huì)導(dǎo)致棧內(nèi)存損壞,有可能會(huì)產(chǎn)生奇奇怪怪的錯(cuò)誤。
例如:
- void func() {
- char buffer[10];
- int counter;
- lstrcpy(buffer, "abcdefghik"); // 需要拷貝11字節(jié)
- }
tips:不要將Debug庫(kù)和Release庫(kù)混合在一起,可能會(huì)出問(wèn)題,至于為啥會(huì)出問(wèn)題,知道的可在下方評(píng)論區(qū)留言,一般都是有兩個(gè)目錄,Debug目錄和Release目錄。
回到開(kāi)篇提到的那兩個(gè)問(wèn)題:
參考資料
http://www.flounder.com/debug_release.htm
https://vinayakgarg.wordpress.com/2012/03/31/difference-between-debug-build-and-release-build/
https://www.xspdf.com/resolution/312312.html
https://docs.oracle.com/cd/E19205-01/820-1214/bkacs/index.html
https://www.cnblogs.com/huaping-audio/archive/2008/08/23/1274833.html
https://www.zhihu.com/question/27090458
https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html