簡介
各種軟件對(duì)于性能的需求可能會(huì)有很大的區(qū)別循签,但是很多應(yīng)用程序都有非常嚴(yán)格的性能需求教翩,這一點(diǎn)并不奇怪阱洪。電影播放器就是一個(gè)很好的例子:如果一個(gè)電影播放器只能以所需要速度的 75% 來播放電影,那么它幾乎就沒什么用處了槽奕。
其他應(yīng)用程序(例如視頻編碼)如果是耗時(shí)非常長的操作嘴纺,最好以 “批處理” 任務(wù)的方式運(yùn)行败晴,此時(shí)啟動(dòng)一個(gè)作業(yè),讓其一直運(yùn)行栽渴,然后我們就可以去干別的事情了尖坤。盡管這些類型的應(yīng)用程序沒有這種硬性性能指標(biāo)的限制,但是提高速度仍然會(huì)帶來很多好處闲擦,例如可以在給定的時(shí)間內(nèi)可以對(duì)更多電影進(jìn)行編碼慢味,在同樣的時(shí)間內(nèi)可以以更高的品質(zhì)進(jìn)行編碼。
通常墅冷,除了最簡單的應(yīng)用程序之外纯路,對(duì)于其他應(yīng)用程序來說,性能越好寞忿,這個(gè)應(yīng)用程序的用處就越大驰唬,也就會(huì)越流行。由于這個(gè)原因腔彰,性能考慮是(也應(yīng)該是)很多應(yīng)用程序開發(fā)人員腦袋中的第一根弦叫编。
不幸的是,很多嘗試讓應(yīng)用程序速度更快的努力都白費(fèi)了霹抛,因?yàn)殚_發(fā)人員通常都是對(duì)自己的軟件進(jìn)行一些小型的優(yōu)化宵溅,而沒有去研究程序在更大的范圍內(nèi)是如何操作的。例如上炎,我們可能會(huì)花費(fèi)大量的時(shí)間來讓某個(gè)特定函數(shù)的運(yùn)行速度達(dá)到原來的兩倍,這一點(diǎn)非常不錯(cuò),但是如果這個(gè)函數(shù)很少被調(diào)用(例如打開文件)藕施,那么將這個(gè)函數(shù)的執(zhí)行時(shí)間從 200ms 減少到 100ms寇损,對(duì)于整個(gè)軟件的總體執(zhí)行時(shí)間來說并不會(huì)有太大的影響。
有效地利用您的時(shí)間的方法是裳食,盡量優(yōu)化軟件中被頻繁調(diào)用的部分矛市。例如,假設(shè)應(yīng)用程序花了 50% 的時(shí)間在字符串處理函數(shù)上诲祸,如果可以對(duì)這些函數(shù)進(jìn)行優(yōu)化浊吏,提高 10% 的效率,那么應(yīng)用程序的總體執(zhí)行時(shí)間就會(huì)改進(jìn) 5%救氯。
因此找田,如果希望能夠有效地對(duì)程序進(jìn)行優(yōu)化,那么精確地了解時(shí)間在應(yīng)用程序中是如何花費(fèi)的着憨,以及真實(shí)的輸入數(shù)據(jù)墩衙,這一點(diǎn)非常重要。這種行為就稱為代碼剖析(code profiling)甲抖。本文將簡要介紹 GNU 編譯器工具包所提供的一種剖析工具漆改,它的名字讓人可以產(chǎn)生無限遐想,叫 GNU profiler(gprof)准谚。本文主要面向那些開放源碼軟件開發(fā)工具的新手挫剑。
gprof 來救援了
在開始介紹如何使用 gprof 之前,需要首先了解一下在整個(gè)開發(fā)周期中柱衔,剖析應(yīng)該在何處進(jìn)行樊破。通常來說,編寫代碼應(yīng)該有 3 個(gè)目標(biāo)秀存,按照重要性的次序分別如下所示:
保證軟件可以正確地工作捶码。這通常是開發(fā)過程的重點(diǎn)。通常或链,如果一個(gè)軟件根本連我們期望它做的事情都實(shí)現(xiàn)不了惫恼,那么即使它運(yùn)行速度非常快澳盐,也根本沒有任何意義祈纯!顯然,正確性在某些情況下可能并不是至關(guān)重要的叼耙;例如腕窥,如果一個(gè)電影播放器可以正確地播放 99% 的電影文件,但是偶然會(huì)有些顯示問題筛婉,那它依然可以使用簇爆。但是通常來說癞松,正確性要遠(yuǎn)遠(yuǎn)比速度更加重要。
保證軟件是可維護(hù)的入蛆。這實(shí)際上是第一個(gè)目標(biāo)的一個(gè)子項(xiàng)响蓉。通常,如果軟件編寫得可維護(hù)性不好哨毁,那么即使它最開始時(shí)可以很好地工作枫甲,很快您(或其他人)在修正 bug 或添加新特性時(shí)可能也會(huì)破壞程序的正確性。
讓軟件可以快速運(yùn)行扼褪。這就是剖析的用武之地想幻。當(dāng)軟件可以正確運(yùn)行之后,我們就可以開始剖析的過程來幫助它更快地運(yùn)行了话浇。
假設(shè)我們現(xiàn)在已經(jīng)有了一個(gè)可以工作的應(yīng)用程序脏毯,接下來讓我們來看一下如何使用 gprof 來精確測(cè)量應(yīng)用程序執(zhí)行過程中時(shí)間都花費(fèi)到什么地方去了,這樣做的目的是了解一下在什么地方進(jìn)行優(yōu)化效果最佳凳枝。
gprof 可以對(duì) C抄沮、C++、Pascal 和 Fortran 77 應(yīng)用程序進(jìn)行剖析岖瑰。本文中的例子使用的是 C叛买。
清單 1. 耗時(shí)的應(yīng)用程序示例
#include
int a(void) {
int i=0,g=0;
while(i++<100000)
{
g+=i;
}
return g;
}
int b(void) {
int i=0,g=0;
while(i++<400000)
{
g+=i;
}
return g;
}
int main(int argc, char** argv)
{
int iterations;
if(argc != 2)
{
printf("Usage %s \\n", argv[0]);
exit(-1);
}
else
iterations = atoi(argv[1]);
printf("No of iterations = %d\\n", iterations);
while(iterations--)
{
a();
b();
}
}
正如我們從代碼中可以看到的,這個(gè)非常簡單的應(yīng)用程序包括兩個(gè)函數(shù):a和b蹋订,它們都處于一個(gè)繁忙的循環(huán)中消耗 CPU 周期率挣。main函數(shù)中采用了一個(gè)循環(huán)來反復(fù)調(diào)用這兩個(gè)函數(shù)。第二個(gè)函數(shù)b循環(huán)的次數(shù)是a函數(shù)的 4 倍露戒,因此我們期望在對(duì)代碼分析完之后椒功,可以看出大概有 20% 的時(shí)間花在了a函數(shù)中,而 80% 的時(shí)間花在了b函數(shù)中智什。下面就開始剖析代碼动漾,并看一下我們的這些期望是否正確。
啟用剖析非常簡單荠锭,只需要在 gcc 編譯標(biāo)志中加上-pg即可旱眯。編譯方法如下:
gcc example1.c -pg -o example1 -O2 -lc
在編譯好這個(gè)應(yīng)用程序之后,可以按照普通方式運(yùn)行這個(gè)程序:
./example1 50000
當(dāng)這個(gè)程序運(yùn)行完之后证九,應(yīng)該會(huì)看到在當(dāng)前目錄中新創(chuàng)建了一個(gè)文件 gmon.out删豺。
使用輸出結(jié)果
首先看一下 “flat profile”,我們可以使用gprof命令獲得它愧怜,這需要為其傳遞可執(zhí)行文件和 gmon.out 文件作為參數(shù)呀页,如下所示:
gprof example1 gmon.out -p
這會(huì)輸出以下內(nèi)容:
清單 2. flat profile 的結(jié)果
Flat profile:
Each sample counts as 0.01 seconds.
% ? cumulative ? self ? ? ? ? ? ? ?self ? ? total
time ? seconds ? seconds ? ?calls ?ms/call ?ms/call ?name
80.24 ? ? 63.85 ? ?63.85 ? ?50000 ? ? 1.28 ? ? 1.28 ?b
20.26 ? ? 79.97 ? ?16.12 ? ?50000 ? ? 0.32 ? ? 0.32 ?a
從這個(gè)輸出結(jié)果中可以看到,正如我們期望的一樣拥坛,b函數(shù)所花費(fèi)的時(shí)間大概是a函數(shù)所花費(fèi)的時(shí)間的 4 倍蓬蝶。真正的數(shù)字并不是十分有用尘分;由于取整舍入錯(cuò)誤,這些數(shù)字可能并不是非常精確丸氛。
聰明的讀者可能會(huì)注意到音诫,很多函數(shù)調(diào)用(例如printf)在這個(gè)輸出中都沒有出現(xiàn)。這是因?yàn)檫@些函數(shù)都是在 C 運(yùn)行時(shí)庫(libc.so)中的雪位,(在本例中)它們都沒有使用-pg進(jìn)行編譯,因此就沒有對(duì)這個(gè)庫中的函數(shù)收集剖析信息梨撞。稍后我們會(huì)回到這個(gè)問題上來雹洗。
接下來我們希望了解的是 “call graph”,這可以通過下面的方式獲得:
gprof example1 gmon.out -q
這會(huì)輸出下面的結(jié)果卧波。
清單 3. Call graph
Call graph (explanation follows)
granularity: each sample hit covers 2 byte(s) for 0.01% of 79.97 seconds
index % time ? ?self ?children ? ?called ? ? name
[1] ? ?100.0 ? ?0.00 ? 79.97 ? ? ? ? ? ? ? ? main [1]
63.85 ? ?0.00 ? 50000/50000 ? ? ? b [2]
16.12 ? ?0.00 ? 50000/50000 ? ? ? a [3]
-----------------------------------------------
63.85 ? ?0.00 ? 50000/50000 ? ? ? main [1]
[2] ? ? 79.8 ? 63.85 ? ?0.00 ? 50000 ? ? ? ? b [2]
-----------------------------------------------
16.12 ? ?0.00 ? 50000/50000 ? ? ? main [1]
[3] ? ? 20.2 ? 16.12 ? ?0.00 ? 50000 ? ? ? ? a [3]
-----------------------------------------------
最后时肿,我們可能會(huì)希望獲得一個(gè) “帶注解的源代碼” 清單,它會(huì)將源代碼輸出到應(yīng)用程序中港粱,并加上每個(gè)函數(shù)被調(diào)用了多少次的注釋螃成。
要使用這種功能,請(qǐng)使用啟用調(diào)試功能的標(biāo)志來編譯源代碼查坪,這樣源代碼就會(huì)被加入可執(zhí)行程序中:
gcc example1.c -g -pg -o example1 -O2 -lc
像以前一樣重新運(yùn)行這個(gè)應(yīng)用程序:
./example1 50000
gprof命令現(xiàn)在應(yīng)該是:
gprof example1 gmon.out -A
這會(huì)輸出下面的結(jié)果:
清單 4. 帶注釋的源代碼
*** File /home/martynh/profarticle/example1.c:
#include
50000 -> int a(void) {
int i=0,g=0;
while(i++<100000)
{
g+=i;
}
return g;
}
50000 -> int b(void) {
int i=0,g=0;
while(i++<400000)
{
g+=i;
}
return g;
}
int main(int argc, char** argv)
##### -> {
int iterations;
if(argc != 2)
{
printf("Usage %s \\n", argv[0]);
exit(-1);
}
else
iterations = atoi(argv[1]);
printf("No of iterations = %d\\n", iterations);
while(iterations--)
{
a();
b();
}
}
Top 10 Lines:
Line ? ? ?Count
3 ? ? ?50000
11 ? ? ?50000
Execution Summary:
3 ? Executable lines in this file
3 ? Lines executed
100.00 ? Percent of the file executed
100000 ? Total number of line executions
33333.33 ? Average executions per line
共享庫的支持
正如在前面曾經(jīng)介紹的寸宏,對(duì)于代碼剖析的支持是由編譯器增加的,因此如果希望從共享庫(包括 C 庫 libc.a)中獲得剖析信息偿曙,就需要使用-pg來編譯這些庫氮凝。幸運(yùn)的是,很多發(fā)行版都提供了已經(jīng)啟用代碼剖析支持而編譯的 C 庫版本(libc_p.a)望忆。
在我使用的發(fā)行版 gentoo 中罩阵,需要將 “profile” 添加到 USE 標(biāo)志中,并重新執(zhí)行emergeglibc启摄。當(dāng)這個(gè)過程完成之后稿壁,就會(huì)看到 /usr/lib/libc_p.a 文件已經(jīng)創(chuàng)建好了。對(duì)于沒有按照標(biāo)準(zhǔn)提供 libc_p 的發(fā)行版本來說歉备,需要檢查它是否可以單獨(dú)安裝傅是,或者可能需要自己下載 glibc 的源代碼并進(jìn)行編譯。
在獲得 libc_p.a 文件之后威创,就可以簡單地重新編譯前面的例子了落午,方法如下:
gcc example1.c -g -pg -o example1 -O2 -lc_p
然后,可以像以前一樣運(yùn)行這個(gè)應(yīng)用程序肚豺,并獲得 flat profile 或 call graph溃斋,應(yīng)該會(huì)看到很多 C 運(yùn)行函數(shù),包括printf(這些函數(shù)在我們的測(cè)試函數(shù)中并不是太重要)吸申。
用戶時(shí)間與內(nèi)核時(shí)間
現(xiàn)在我們已經(jīng)知道如何使用 gprof 了梗劫,接下來可以簡單且有效地對(duì)應(yīng)用程序進(jìn)行分析了享甸,希望可以消除性能瓶頸。
不過現(xiàn)在您可能已經(jīng)注意到了 gprof 的最大缺陷:它只能分析應(yīng)用程序在運(yùn)行過程中所消耗掉的用戶時(shí)間梳侨。通常來說蛉威,應(yīng)用程序在運(yùn)行時(shí)既要花費(fèi)一些時(shí)間來運(yùn)行用戶代碼,也要花費(fèi)一些時(shí)間來運(yùn)行 “系統(tǒng)代碼”走哺,例如內(nèi)核系統(tǒng)調(diào)用蚯嫌。
如果對(duì)清單 1 稍加修改,就可以清楚地看出這個(gè)問題:
清單 5. 為清單 1 添加系統(tǒng)調(diào)用分析功能
#include
int a(void) {
sleep(1);
return 0;
}
int b(void) {
sleep(4);
return 0;
}
int main(int argc, char** argv)
{
int iterations;
if(argc != 2)
{
printf("Usage %s \\n", argv[0]);
exit(-1);
}
else
iterations = atoi(argv[1]);
printf("No of iterations = %d\\n", iterations);
while(iterations--)
{
a();
b();
}
}
正如您可以看到的丙躏,我們對(duì)清單 1 中的代碼進(jìn)行了修改择示,現(xiàn)在a函數(shù)和b函數(shù)不再只處理繁忙的循環(huán)了,而是分別調(diào)用 C 運(yùn)行時(shí)函數(shù)sleep來掛起執(zhí)行 1 秒和 4 秒晒旅。
像以前一樣編譯這個(gè)應(yīng)用程序:
gcc example2.c -g -pg -o example2 -O2 -lc_p
并讓這個(gè)程序循環(huán) 30 次:
./example2 30
所生成的 flat profile 如下所示:
清單 6. flat profile 顯示了系統(tǒng)調(diào)用的結(jié)果
Flat profile:
Each sample counts as 0.01 seconds.
no time accumulated
% ? cumulative ? self ? ? ? ? ? ? ?self ? ? total
time ? seconds ? seconds ? ?calls ?Ts/call ?Ts/call ?name
0.00 ? ? ?0.00 ? ? 0.00 ? ? ?120 ? ? 0.00 ? ? 0.00 ?sigprocmask
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? 61 ? ? 0.00 ? ? 0.00 ?__libc_sigaction
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? 61 ? ? 0.00 ? ? 0.00 ?sigaction
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? 60 ? ? 0.00 ? ? 0.00 ?nanosleep
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? 60 ? ? 0.00 ? ? 0.00 ?sleep
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? 30 ? ? 0.00 ? ? 0.00 ?a
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? 30 ? ? 0.00 ? ? 0.00 ?b
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? 21 ? ? 0.00 ? ? 0.00 ?_IO_file_overflow
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?3 ? ? 0.00 ? ? 0.00 ?_IO_new_file_xsputn
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?2 ? ? 0.00 ? ? 0.00 ?_IO_new_do_write
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?2 ? ? 0.00 ? ? 0.00 ?__find_specmb
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?2 ? ? 0.00 ? ? 0.00 ?__guard_setup
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?_IO_default_xsputn
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?_IO_doallocbuf
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?_IO_file_doallocate
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?_IO_file_stat
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?_IO_file_write
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?_IO_setb
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?____strtol_l_internal
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?___fxstat64
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?__cxa_atexit
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?__errno_location
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?__new_exitfn
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?__strtol_internal
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?_itoa_word
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?_mcleanup
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?atexit
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?atoi
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?exit
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?flockfile
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?funlockfile
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?main
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?mmap
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?moncontrol
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?new_do_write
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?printf
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?setitimer
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?vfprintf
0.00 ? ? ?0.00 ? ? 0.00 ? ? ? ?1 ? ? 0.00 ? ? 0.00 ?write
如果對(duì)這個(gè)輸出結(jié)果進(jìn)行分析栅盲,我們就會(huì)看到,盡管 profiler 已經(jīng)記錄了每個(gè)函數(shù)被調(diào)用的確切次數(shù)废恋,但是為這些函數(shù)記錄的時(shí)間(實(shí)際上是所有函數(shù))都是 0.00谈秫。這是因?yàn)閟leep函數(shù)實(shí)際上是執(zhí)行了一次對(duì)內(nèi)核空間的調(diào)用,從而將應(yīng)用程序的執(zhí)行掛起了鱼鼓,然后有效地暫停執(zhí)行拟烫,并等待內(nèi)核再次將其喚醒。由于花在用戶空間執(zhí)行的時(shí)間與花在內(nèi)核中睡眠的時(shí)間相比非常小蚓哩,因此就被取整成零了构灸。其原因是 gprof 僅僅是通過以固定的周期對(duì)程序運(yùn)行時(shí)間進(jìn)行采樣測(cè)量來工作的。因此岸梨,當(dāng)程序不運(yùn)行時(shí)喜颁,就不會(huì)對(duì)程序進(jìn)行采樣測(cè)量。
這實(shí)際上是一把雙刃劍曹阔。從一個(gè)方面來說半开,這使得有些程序非常難以進(jìn)行優(yōu)化,例如花費(fèi)大部分時(shí)間在內(nèi)核空間的程序赃份,或者由于外部因素(例如操作系統(tǒng)的 I/O 子系統(tǒng)過載)而運(yùn)行得非常慢的程序寂拆。從另一個(gè)方面來說,這意味著剖析不會(huì)受到系統(tǒng)中其他事件的影響(例如另外一個(gè)用戶使用了大量的 CPU 時(shí)間)抓韩。
通常纠永,有一個(gè)很好的基準(zhǔn)測(cè)試可以用來查看 gprof 對(duì)于幫助對(duì)應(yīng)用程序進(jìn)行優(yōu)化是多么有用,方法是在time命令下面執(zhí)行它谒拴。這個(gè)命令會(huì)顯示一個(gè)應(yīng)用程序運(yùn)行完成需要多少時(shí)間尝江,并可以測(cè)量它在用戶空間和內(nèi)核空間各花費(fèi)了多少時(shí)間。
如果查看一下清單 2 中的例子:
time ./example2 30
輸出結(jié)果應(yīng)該如下所示:
清單 7. time 命令的輸出結(jié)果
No of iterations = 30
real ? ?2m30.295s
user ? ?0m0.000s
sys ? ? 0m0.004s
我們可以看出幾乎沒有多少時(shí)間被花費(fèi)在執(zhí)行用戶空間的代碼上英上,因此 gprof 在此處不會(huì)非常有用炭序。
結(jié)束語
盡管 gprof 存在上面的限制啤覆,但是它對(duì)于優(yōu)化代碼來說依然是個(gè)非常有用的工具,如果您的代碼大部分是用戶空間 CPU 密集型的惭聂,它的用處就更加明顯窗声。首先使用time來運(yùn)行程序從而判斷 gprof 是否能產(chǎn)生有用信息是個(gè)好主意。
如果 gprof 不適合您的剖析需要辜纲,那么還有其他一些工具可以克服 gprof 部分缺陷笨觅,包括 OProfile 和 Sysprof (請(qǐng)參看參考資料中有關(guān)這些工具信息的鏈接)。
從另一個(gè)方面來說耕腾,假設(shè)我們已經(jīng)安裝了 gcc屋摇,gprof 相對(duì)于其他工具來說,一個(gè)主要的優(yōu)點(diǎn)是很可能早已在 Linux 機(jī)器上安裝了需要使用的工具幽邓。
Gprof產(chǎn)生的信息解釋:
%time ? ?
該函數(shù)消耗時(shí)間占程序所有時(shí)間百分比
Cumulative Seconds
程序的累積執(zhí)行時(shí)間 (只是包括gprof能夠監(jiān)控到的函數(shù))
Self seconds
該函數(shù)本身執(zhí)行時(shí)間(所有被調(diào)用次數(shù)的合共時(shí)間)
Calls
函數(shù)被調(diào)用次數(shù)
Self ?TS/call
函數(shù)平均執(zhí)行時(shí)間(不包括被調(diào)用時(shí)間)(函數(shù)的單次執(zhí)行時(shí)間)
TotalTS/call
函數(shù)平均執(zhí)行時(shí)間(包括被調(diào)用時(shí)間)
name
(函數(shù)的單次執(zhí)行時(shí)間)函數(shù)名
Call Graph的字段含義:
Index ?
索引值
%time
函數(shù)消耗時(shí)間占所有時(shí)間百分比
Self
函數(shù)本身執(zhí)行時(shí)間
Children
執(zhí)行子函數(shù)所用時(shí)間
Called
被調(diào)用次數(shù)
Name
函數(shù)名