日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

GCC使用詳解(2)

 楚暮四 2010-05-06

代碼優(yōu)化
    代碼優(yōu)化指的是編譯器通過(guò)分析源代碼,找出其中尚未達(dá)到最優(yōu)的部分,然后對(duì)其重新進(jìn)行組合,目的是改善程序的執(zhí)行性能。GCC提供的代碼優(yōu)化功能非常強(qiáng) 大,它通過(guò)編譯選項(xiàng)-On來(lái)控制優(yōu)化代碼的生成,其中n是一個(gè)代表優(yōu)化級(jí)別的整數(shù)。對(duì)于不同版本的GCC來(lái)講,n的取值范圍及其對(duì)應(yīng)的優(yōu)化效果可能并不完 全相同,比較典型的范圍是從0變化到2或3。
    編譯時(shí)使用選項(xiàng)-O可以告訴GCC同時(shí)減小代碼的長(zhǎng)度和執(zhí)行時(shí)間,其效果等價(jià)于-O1。在這一級(jí)別上能夠進(jìn)行的優(yōu)化類型雖然取決于目標(biāo)處理器,但一般都會(huì) 包括線程跳轉(zhuǎn)(Thread Jump)和延遲退棧(Deferred Stack Pops)兩種優(yōu)化。選項(xiàng)-O2告訴GCC除了完成所有-O1級(jí)別的優(yōu)化之外,同時(shí)還要進(jìn)行一些額外的調(diào)整工作,如處理器指令調(diào)度等。選項(xiàng)-O3則除了完 成所有-O2級(jí)別的優(yōu)化之外,還包括循環(huán)展開(kāi)和其它一些與處理器特性相關(guān)的優(yōu)化工作。通常來(lái)說(shuō),數(shù)字越大優(yōu)化的等級(jí)越高,同時(shí)也就意味著程序的運(yùn)行速度越 快。許多Linux程序員都喜歡使用-O2選項(xiàng),因?yàn)樗趦?yōu)化長(zhǎng)度、編譯時(shí)間和代碼大小之間,取得了一個(gè)比較理想的平衡點(diǎn)。
    下面通過(guò)具體實(shí)例來(lái)感受一下GCC的代碼優(yōu)化功能,所用程序如清單3所示。
    清單3:optimize.c

#include <stdio.h>
int main(void)
{
  double counter;
  double result;
  double temp;
  for (counter = 0;
   counter < 2000.0 * 2000.0 * 2000.0  / 20.0 + 2020;
   counter += (5 - 1) / 4) {
    temp = counter / 1979;
    result  = counter;   
  }
  printf("Result is %lf\n", result);
  return 0;
}

    首先不加任何優(yōu)化選項(xiàng)進(jìn)行編譯:

# gcc -Wall optimize.c -o optimize

    借助Linux提供的time命令,可以大致統(tǒng)計(jì)出該程序在運(yùn)行時(shí)所需要的時(shí)間:

# time ./optimize
Result is 400002019.000000
real    0m14.942s
user    0m14.940s
sys     0m0.000s

    接下去使用優(yōu)化選項(xiàng)來(lái)對(duì)代碼進(jìn)行優(yōu)化處理:

# gcc -Wall -O optimize.c -o optimize

    在同樣的條件下再次測(cè)試一下運(yùn)行時(shí)間:

# time ./optimize
Result is 400002019.000000
real    0m3.256s
user    0m3.240s
sys     0m0.000s

    對(duì)比兩次執(zhí)行的輸出結(jié)果不難看出,程序的性能的確得到了很大幅度的改善,由原來(lái)的14秒縮短到了3秒。這個(gè)例子是專門(mén)針對(duì)GCC的優(yōu)化功能而設(shè)計(jì)的,因此 優(yōu)化前后程序的執(zhí)行速度發(fā)生了很大的改變。盡管GCC的代碼優(yōu)化功能非常強(qiáng)大,但作為一名優(yōu)秀的Linux程序員,首先還是要力求能夠手工編寫(xiě)出高質(zhì)量的 代碼。如果編寫(xiě)的代碼簡(jiǎn)短,并且邏輯性強(qiáng),編譯器就不會(huì)做更多的工作,甚至根本用不著優(yōu)化。
    優(yōu)化雖然能夠給程序帶來(lái)更好的執(zhí)行性能,但在如下一些場(chǎng)合中應(yīng)該避免優(yōu)化代碼:

  •  程序開(kāi)發(fā)的時(shí)候 優(yōu)化等級(jí)越高,消耗在編譯上的時(shí)間就越長(zhǎng),因此在開(kāi)發(fā)的時(shí)候最好不要使用優(yōu)化選項(xiàng),只有到軟件發(fā)行或開(kāi)發(fā)結(jié)束的時(shí)候,才考慮對(duì)最終生成的代碼進(jìn)行優(yōu)化。
  •  資源受限的時(shí)候 一些優(yōu)化選項(xiàng)會(huì)增加可執(zhí)行代碼的體積,如果程序在運(yùn)行時(shí)能夠申請(qǐng)到的內(nèi)存資源非常緊張(如一些實(shí)時(shí)嵌入式設(shè)備),那就不要對(duì)代碼進(jìn)行優(yōu)化,因?yàn)橛蛇@帶來(lái)的負(fù)面影響可能會(huì)產(chǎn)生非常嚴(yán)重的后果。
  •  跟蹤調(diào)試的時(shí)候 在對(duì)代碼進(jìn)行優(yōu)化的時(shí)候,某些代碼可能會(huì)被刪除或改寫(xiě),或者為了取得更佳的性能而進(jìn)行重組,從而使跟蹤和調(diào)試變得異常困難。

調(diào)試
    一個(gè)功能強(qiáng)大的調(diào)試器不僅為程序員提供了跟蹤程序執(zhí)行的手段,而且還可以幫助程序員找到解決問(wèn)題的方法。對(duì)于Linux程序員來(lái)講,GDB(GNU Debugger)通過(guò)與GCC的配合使用,為基于Linux的軟件開(kāi)發(fā)提供了一個(gè)完善的調(diào)試環(huán)境。
    默認(rèn)情況下,GCC在編譯時(shí)不會(huì)將調(diào)試符號(hào)插入到生成的二進(jìn)制代碼中,因?yàn)檫@樣會(huì)增加可執(zhí)行文件的大小。如果需要在編譯時(shí)生成調(diào)試符號(hào)信息,可以使用 GCC的-g或者-ggdb選項(xiàng)。GCC在產(chǎn)生調(diào)試符號(hào)時(shí),同樣采用了分級(jí)的思路,開(kāi)發(fā)人員可以通過(guò)在-g選項(xiàng)后附加數(shù)字1、2或3來(lái)指定在代碼中加入調(diào) 試信息的多少。默認(rèn)的級(jí)別是2(-g2),此時(shí)產(chǎn)生的調(diào)試信息包括擴(kuò)展的符號(hào)表、行號(hào)、局部或外部變量信息。級(jí)別3(-g3)包含級(jí)別2中的所有調(diào)試信 息,以及源代碼中定義的宏。級(jí)別1(-g1)不包含局部變量和與行號(hào)有關(guān)的調(diào)試信息,因此只能夠用于回溯跟蹤和堆棧轉(zhuǎn)儲(chǔ)之用?;厮莞欀傅氖潜O(jiān)視程序在運(yùn) 行過(guò)程中的函數(shù)調(diào)用歷史,堆棧轉(zhuǎn)儲(chǔ)則是一種以原始的十六進(jìn)制格式保存程序執(zhí)行環(huán)境的方法,兩者都是經(jīng)常用到的調(diào)試手段。
    GCC產(chǎn)生的調(diào)試符號(hào)具有普遍的適應(yīng)性,可以被許多調(diào)試器加以利用,但如果使用的是GDB,那么還可以通過(guò)-ggdb選項(xiàng)在生成的二進(jìn)制代碼中包含GDB 專用的調(diào)試信息。這種做法的優(yōu)點(diǎn)是可以方便GDB的調(diào)試工作,但缺點(diǎn)是可能導(dǎo)致其它調(diào)試器(如DBX)無(wú)法進(jìn)行正常的調(diào)試。選項(xiàng)-ggdb能夠接受的調(diào)試 級(jí)別和-g是完全一樣的,它們對(duì)輸出的調(diào)試符號(hào)有著相同的影響。
    需要注意的是,使用任何一個(gè)調(diào)試選項(xiàng)都會(huì)使最終生成的二進(jìn)制文件的大小急劇增加,同時(shí)增加程序在執(zhí)行時(shí)的開(kāi)銷(xiāo),因此調(diào)試選項(xiàng)通常僅在軟件的開(kāi)發(fā)和調(diào)試階段使用。調(diào)試選項(xiàng)對(duì)生成代碼大小的影響從下面的對(duì)比過(guò)程中可以看出來(lái):

# gcc optimize.c -o optimize
# ls optimize -l
-rwxrwxr-x  1 xiaowp   xiaowp  11649 Nov 20 08:53 optimize  (未加調(diào)試選項(xiàng))
# gcc -g optimize.c -o optimize
# ls optimize -l
-rwxrwxr-x  1 xiaowp   xiaowp  15889 Nov 20 08:54 optimize  (加入調(diào)試選項(xiàng))

    雖然調(diào)試選項(xiàng)會(huì)增加文件的大小,但事實(shí)上Linux中的許多軟件在測(cè)試版本甚至最終發(fā)行版本中仍然使用了調(diào)試選項(xiàng)來(lái)進(jìn)行編譯,這樣做的目的是鼓勵(lì)用戶在發(fā)現(xiàn)問(wèn)題時(shí)自己動(dòng)手解決,是Linux的一個(gè)顯著特色。

下面還是通過(guò)一個(gè)具體的實(shí)例說(shuō)明如何利用調(diào)試符號(hào)來(lái)分析錯(cuò)誤,所用程序見(jiàn)清單4所示。
    清單4:crash.c

#include <stdio.h>
int main(void)
{
  int input =0;
  printf("Input an integer:");
  scanf("%d", input);
  printf("The integer you input is %d\n", input);
  return 0;
}

    編譯并運(yùn)行上述代碼,會(huì)產(chǎn)生一個(gè)嚴(yán)重的段錯(cuò)誤(Segmentation fault)如下:

# gcc -g crash.c -o crash
# ./crash
Input an integer:10
Segmentation fault

    為了更快速地發(fā)現(xiàn)錯(cuò)誤所在,可以使用GDB進(jìn)行跟蹤調(diào)試,方法如下:

# gdb crash
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
……
(gdb)

    當(dāng)GDB提示符出現(xiàn)的時(shí)候,表明GDB已經(jīng)做好準(zhǔn)備進(jìn)行調(diào)試了,現(xiàn)在可以通過(guò)run命令讓程序開(kāi)始在GDB的監(jiān)控下運(yùn)行:

(gdb) run
Starting program: /home/xiaowp/thesis/gcc/code/crash
Input an integer:10
Program received signal SIGSEGV, Segmentation fault.
0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6

    仔細(xì)分析一下GDB給出的輸出結(jié)果不難看出,程序是由于段錯(cuò)誤而導(dǎo)致異常中止的,說(shuō)明內(nèi)存操作出了問(wèn)題,具體發(fā)生問(wèn)題的地方是在調(diào)用 _IO_vfscanf_internal ( )的時(shí)候。為了得到更加有價(jià)值的信息,可以使用GDB提供的回溯跟蹤命令backtrace,執(zhí)行結(jié)果如下:

(gdb) backtrace
#0  0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6
#1  0xbffff0c0 in ?? ()
#2  0x4008e0ba in scanf () from /lib/libc.so.6
#3  0x08048393 in main () at crash.c:11
#4  0x40042917 in __libc_start_main () from /lib/libc.so.6

    跳過(guò)輸出結(jié)果中的前面三行,從輸出結(jié)果的第四行中不難看出,GDB已經(jīng)將錯(cuò)誤定位到crash.c中的第11行了?,F(xiàn)在仔細(xì)檢查一下:

(gdb) frame 3
#3  0x08048393 in main () at crash.c:11
11       scanf("%d", input);

    使用GDB提供的frame命令可以定位到發(fā)生錯(cuò)誤的代碼段,該命令后面跟著的數(shù)值可以在backtrace命令輸出結(jié)果中的行首找到?,F(xiàn)在已經(jīng)發(fā)現(xiàn)錯(cuò)誤所在了,應(yīng)該將

scanf("%d", input);

改為

scanf("%d", &input);

    完成后就可以退出GDB了,命令如下:

(gdb) quit

    GDB的功能遠(yuǎn)遠(yuǎn)不止如此,它還可以單步跟蹤程序、檢查內(nèi)存變量和設(shè)置斷點(diǎn)等。
    調(diào)試時(shí)可能會(huì)需要用到編譯器產(chǎn)生的中間結(jié)果,這時(shí)可以使用-save-temps選項(xiàng),讓GCC將預(yù)處理代碼、匯編代碼和目標(biāo)代碼都作為文件保存起來(lái)。如 果想檢查生成的代碼是否能夠通過(guò)手工調(diào)整的辦法來(lái)提高執(zhí)行性能,在編譯過(guò)程中生成的中間文件將會(huì)很有幫助,具體情況如下:

# gcc -save-temps foo.c -o foo
# ls foo*
foo  foo.c  foo.i  foo.s

    GCC支持的其它調(diào)試選項(xiàng)還包括-p和-pg,它們會(huì)將剖析(Profiling)信息加入到最終生成的二進(jìn)制代碼中。剖析信息對(duì)于找出程序的性能瓶頸很 有幫助,是協(xié)助Linux程序員開(kāi)發(fā)出高性能程序的有力工具。在編譯時(shí)加入-p選項(xiàng)會(huì)在生成的代碼中加入通用剖析工具(Prof)能夠識(shí)別的統(tǒng)計(jì)信息,而 -pg選項(xiàng)則生成只有GNU剖析工具(Gprof)才能識(shí)別的統(tǒng)計(jì)信息。

    最后提醒一點(diǎn),雖然GCC允許在優(yōu)化的同時(shí)加入調(diào)試符號(hào)信息,但優(yōu)化后的代碼對(duì)于調(diào)試本身而言將是一個(gè)很大的挑戰(zhàn)。代碼在經(jīng)過(guò)優(yōu)化之后,在源程序中聲明和 使用的變量很可能不再使用, 控制流也可能會(huì)突然跳轉(zhuǎn)到意外的地方,循環(huán)語(yǔ)句有可能因?yàn)檠h(huán)展開(kāi)而變得到處都有,所有這些對(duì)調(diào)試來(lái)講都將是一場(chǎng)噩夢(mèng)。建議在調(diào)試的時(shí)候最好不使用任何優(yōu) 化選項(xiàng),只有當(dāng)程序在最終發(fā)行的時(shí)候才考慮對(duì)其進(jìn)行優(yōu)化。
   上次的培訓(xùn)園地中介紹了GCC的編譯過(guò)程、警告提示功能、庫(kù)依賴、代碼優(yōu)化和程序調(diào)試六個(gè)方面的內(nèi)容。這期是最后的一部分內(nèi)容。
加速
    在將源代碼變成可執(zhí)行文件的過(guò)程中,需要經(jīng)過(guò)許多中間步驟,包含預(yù)處理、編譯、匯編和連接。這些過(guò)程實(shí)際上是由不同的程序負(fù)責(zé)完成的。大多數(shù)情況下GCC可以為L(zhǎng)inux程序員完成所有的后臺(tái)工作,自動(dòng)調(diào)用相應(yīng)程序進(jìn)行處理。
    這樣做有一個(gè)很明顯的缺點(diǎn),就是GCC在處理每一個(gè)源文件時(shí),最終都需要生成好幾個(gè)臨時(shí)文件才能完成相應(yīng)的工作,從而無(wú)形中導(dǎo)致處理速度變慢。例 如,GCC在處理一個(gè)源文件時(shí),可能需要一個(gè)臨時(shí)文件來(lái)保存預(yù)處理的輸出、一個(gè)臨時(shí)文件來(lái)保存編譯器的輸出、一個(gè)臨時(shí)文件來(lái)保存匯編器的輸出,而讀寫(xiě)這些 臨時(shí)文件顯然需要耗費(fèi)一定的時(shí)間。當(dāng)軟件項(xiàng)目變得非常龐大的時(shí)候,花費(fèi)在這上面的代價(jià)可能會(huì)變得很沉重。
    解決的辦法是,使用Linux提供的一種更加高效的通信方式—管道。它可以用來(lái)同時(shí)連接兩個(gè)程序,其中一個(gè)程序的輸出將被直接作為另一個(gè)程序的輸入,這樣就可以避免使用臨時(shí)文件,但編譯時(shí)卻需要消耗更多的內(nèi)存。
    在編譯過(guò)程中使用管道是由GCC的-pipe選項(xiàng)決定的。下面的這條命令就是借助GCC的管道功能來(lái)提高編譯速度的:

# gcc -pipe foo.c -o foo

    在編譯小型工程時(shí)使用管道,編譯時(shí)間上的差異可能還不是很明顯,但在源代碼非常多的大型工程中,差異將變得非常明顯。
文件擴(kuò)展名
    在使用GCC的過(guò)程中,用戶對(duì)一些常用的擴(kuò)展名一定要熟悉,并知道其含義。為了方便大家學(xué)習(xí)使用GCC,在此將這些擴(kuò)展名羅列如下:
.c C原始程序;
.C C++原始程序;
.cc C++原始程序;
.cxx C++原始程序;
.m Objective-C原始程序;
.i 已經(jīng)過(guò)預(yù)處理的C原始程序;
.ii 已經(jīng)過(guò)預(yù)處理之C++原始程序;
.s 組合語(yǔ)言原始程序;
.S 組合語(yǔ)言原始程序;
.h 預(yù)處理文件(標(biāo)頭文件);
.o 目標(biāo)文件;
.a 存檔文件。

GCC常用選項(xiàng)
    GCC作為L(zhǎng)inux下C/C++重要的編譯環(huán)境,功能強(qiáng)大,編譯選項(xiàng)繁多。為了方便大家日后編譯方便,在此將常用的選項(xiàng)及說(shuō)明羅列出來(lái)如下:
-c 通知GCC取消鏈接步驟,即編譯源碼并在最后生成目標(biāo)文件;
-Dmacro 定義指定的宏,使它能夠通過(guò)源碼中的#ifdef進(jìn)行檢驗(yàn);
-E 不經(jīng)過(guò)編譯預(yù)處理程序的輸出而輸送至標(biāo)準(zhǔn)輸出;
-g3 獲得有關(guān)調(diào)試程序的詳細(xì)信息,它不能與-o選項(xiàng)聯(lián)合使用;
-Idirectory 在包含文件搜索路徑的起點(diǎn)處添加指定目錄;
-llibrary 提示鏈接程序在創(chuàng)建最終可執(zhí)行文件時(shí)包含指定的庫(kù);
-O、-O2、-O3 將優(yōu)化狀態(tài)打開(kāi),該選項(xiàng)不能與-g選項(xiàng)聯(lián)合使用;
-S 要求編譯程序生成來(lái)自源代碼的匯編程序輸出;
-v 啟動(dòng)所有警報(bào);
-Wall 在發(fā)生警報(bào)時(shí)取消編譯操作,即將警報(bào)看作是錯(cuò)誤;
-Werror 在發(fā)生警報(bào)時(shí)取消編譯操作,即把報(bào)警當(dāng)作是錯(cuò)誤;
-w 禁止所有的報(bào)警。

小結(jié)
    GCC是在Linux下開(kāi)發(fā)程序時(shí)必須掌握的工具之一。本文對(duì)GCC做了一個(gè)簡(jiǎn)要的介紹,主要講述了如何使用GCC編譯程序、產(chǎn)生警告信息、調(diào)試程序和加 快GCC的編譯速度。對(duì)所有希望早日跨入Linux開(kāi)發(fā)者行列的人來(lái)說(shuō),GCC就是成為一名優(yōu)秀的Linux程序員的起跑線。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多