Linux 中的計時

來源:互聯網
上載者:User
簡介: 本文描述了Linux系統中一些與計時相關的問題和解決方案。因為在學習和研究的過程中我們經常需要統計程式或程式段的耗時,評估它們的效能好壞。因而這些問題對於我們來說,經常會遇到。掌握多種計時方法對於開發人員或科研工作者來說都是必須掌握的一項技能。本文解決了在Linux計時當中經常會遇到的一些技術問題,相信會對他人的工作有所協助。

實際上,可能還會有其它一些方法可以完成本文討論的任務。我僅討論了我所使用的方法,這並不意味著除此之外的其它方法就很差勁,只不過對我來說這些方法相對簡單有效而已。

Linux中的時間

在Linux系統中,時間扮演著一個非常重要的角色,它幾乎無處不在。開機時,會顯示如下的資訊:

Last login: Tue Sep 23 22:12:50 from 192.168.6.100

關機時,我們可以使用shutdown命令指定何時或多長時間後機器將會定時關閉。我們有可能還會設定一台Linux時間伺服器與互連網上的一級或二級時間伺服器同步,總之,在Linux系統中,我們必須要瞭解時間。

實際上,linux系統有著自己的計時器時鐘。可以實驗一下,分別執行date和/sbin/clock(或sbin/hwclock)命令,得到的時間是不同的。

[grandiose@Bygone grandiose]$ date Sun Sep 28 21:11:02 EDT 2003 [grandiose@Bygone grandiose]$ /sbin/clock Sun 28 Sep 2003 09:07:07 PM EDT -0.466994 seconds

當你以 root 身份改變了系統時間之後,請記住以 clock -w 來將系統時間寫入 CMOS 中。

回頁首

使用C語言進行計時

在使用者空間中可以使用C語言函數gettimeofday 得到時間,它的調用格式是:

#include <sys/time.h> int gettimeofday(struct timeval *tv, struct timezone *tz); int settimeofday(const struct timeval *tv , const struct timezone *tz); 結構timeval的定義為: strut timeval { long tv_sec; /* 秒數 */ long tv_usec; /* 微秒數 */ };

可以看出,使用這種方式計時,精度可達微秒,也就是10-6秒。進行計時的時候,我們需要前後調用兩次gettimeofday,然後計算中間的差值:

gettimeofday( &start, NULL ); foo(); gettimeofday( &end, NULL ); timeuse = 1000000 * ( end.tv_sec - start.tv_sec ) + end.tv_usec - start.tv_usec; timeuse /= 1000000;

回頁首

Shell計時

在Linux的Shell下,我們經常也使用Shell內建的time命令和GNU版的time命令來測試程式啟動並執行時間。

內建的time提供的參數選項比較少,而GNU的time則提供了豐富的參數選項,包括指定輸出檔案等功能。

[grandiose@Bygone grandiose]$ /usr/bin/time --output=foo.txt foo

上句只有時間資訊輸出到了foo.txt檔案中,如果想要包括foo執行的結果,就需要按下句這樣使用:

[grandiose@Bygone grandiose]$ /usr/bin/time --output=foo.txt --append foo >foo.txt

如果想要控制輸出時間的格式,可以使用-f開關進行格式化:

[grandiose@Bygone grandiose]$ /usr/bin/time --output=foo.txt -f "\\t%E real,\\t%U user,\\t%S sys" foo

如果仍需要使用Shell內建的time命令,可以用下面一句將結果輸出至檔案:

[grandiose@Bygone grandiose]$ (time foo) 2>foo.txt

這樣寫是因為內建命令time的輸出是到標準錯誤的,檔案描述符2表示標準錯誤stderr。如果還想要包括foo執行的結果,就要這樣:

[grandiose@Bygone grandiose]$ (time foo) >foo.txt 2>&1

其中2>&1的含義是2與1 相同,一起送入foo.txt中。

nohup命令可以保證進程在退出系統之後仍能運行,這是它的常規用法。我們也可以這樣使用nohup:

[grandiose@Bygone grandiose]$ nohup time foo

結果全部輸出至nohup.out,也包括程式啟動並執行時間資訊。可以使用下面的語句將時間資訊輸出至檔案foo.txt中。

[grandiose@Bygone grandiose]$ tail -2 nohup.out > foo.txt

為了保證和POSIX一致,輸出的時間格式為(nohup.out中的內容除外):

real 0m0.007s user 0m0.002s sys 0m0.004s

我們可以使用linux下面一些過濾命令如awk、sed、grep、tr等過濾出我們想要得到的內容,例如想要得到real段對應的時間:

[grandiose@Bygone grandiose]$ grep real foo.txt | cut -f2,或者 [grandiose@Bygone grandiose]$ sed -n '2p' foo.txt | cut -f2

在Shell下,輸出的時間精度為毫秒級,如果需要微秒級的計時,那就應該在程式中進行處理。

回頁首

核心空間中的計時

如果要定製自己的裝置驅動程式,可能就會用到核心裡的計時功能。Linux核心空間中的計時與使用者空間的計時不太相同。在核心空間裡,有一個全域變數Jiffies維護著當前的時間。與系統時鐘有關的調用有(新的定時服務):

#include <asm/param.h> #include <linux/timer.h> void add_timer(struct timer_list * timer); int del_timer(struct timer_list * timer); inline void init_timer(struct timer_list * timer);

結構struct timer_list的定義為:

struct timer_list { struct timer_list *next; struct timer_list *prev; unsigned long expires; unsigned long data; void (*function)(unsigned long d); };

其中到期時間expires是要執行function的時間。一般在調用add_timer時jiffies = jiffies + num,表示在num個系統最小時間間隔後執行function。系統最小時間間隔與所用的硬體平台有關, 在核心裡定義了常數HZ表示一秒內最小時間間隔的數目,則num*HZ表示num 秒。系統計時到預定時間就調用function,並把此子程式從定時隊列裡刪除, 因此如果想要每隔一定時間間隔執行一次的話,就必須在function裡再一次調用add_timer。function的參數d即為timer裡面的
data項。

Jiffies的計時精度是百分之一秒,如果在核心中需要更為精確的計時,就需要用到time_calls.h中的函數,它們可用於高精度的時間計算。

回頁首

補充

有的時候,我們需要較為精確地得出被測目標的已耗用時間,這時一般需要多次運行取均值以消除誤差。

gettimeofday( &start, NULL ); for ( int i = 0; i< 10; i++ ) foo(); gettimeofday( &end, NULL ); timeuse = 1000000 * ( end.tv_sec - start.tv_sec ) + end.tv_usec - start.tv_usec; timeuse /= 10000000;

上面的統計實際也引入了新的誤差,當迴圈指令的執行時間與foo()相比可忽略的話,這種計時才是可以接受的;否則我們就要除去迴圈指令的執行時間,才會得到正確的統計計時。

在Linux Shell下,如果統計次數較少,則可以:

for i in 1 2 3 4 5 6 7 8 9 10 do (time foo) 2>foo.tmp grep real foo.tmp | cut -f2 >> foo.txt done

如果計時次數較多,則需要:

i=1 while [ $i -le 100 ] do (time foo) 2>foo.tmp grep real foo.tmp | cut -f2 >> foo.txt i=`expr $i + 1` done

寫進foo.txt的內容如果手動來計算平均值,會比較費時,我們可以寫一段Shell指令碼或用C語言來讀取檔案,計算其均值。

/*耗時中分部總和*/ cut -d'm' -f1 foo.txt > foo.tmp sum=0 while read line do sum=$(echo "$sum+$line" | bc -l) done < foo.tmp echo $sum /*耗時中秒部總和*/ cut -d'm' -f2 foo.txt | tr -d 's'> foo.tmp sum=0 while read line do sum=$(echo "$sum+$line" |
bc -l) done < foo.tmp echo $sum

計算出分部與秒部總和之後,然後再手動計算平均值,這樣要容易得多。注意,上面沒有使用expr進行計算的原因,是因為expr只能支援整型值。在Linux shell下,如果要計算浮點數,就需要使用bc或者是gexpr。

回頁首

結束語

實際上,我們還可以使用諸如Perl、Python等多種語言在Linux系統中進行計時。選擇何種工具或語言進行計時,這與被測程式或程式段的類型以及它們的編寫語言相關。綜合考慮精度、已耗用時間、運行次數等要求,才能合理可靠地得出程式的已耗用時間。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.