Linux 中的計時
本文描述了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系統中進行計時。選擇何種工具或語言進行計時,這與被測程式或程式段的型別以及它們的編寫語言相關。綜合考慮精度、執行時間、執行次數等要求,才能合理可靠地得出程式的執行時間。
參考資料
- 請閱讀 RunTime:High-performance programming techniques on Linux and Windows 2000,裡面有關於Linux和Windows平臺下計時效能開銷的一些討論。
- 請參閱網站 中文Linux知識庫。
- 有關gexpr的資訊,請參閱網站 Gexpr。
- Matt Welsh & Lar Kaufman,Running Linux,USA:O'Reilly & Associates,1995
- 在 developerWorks Linux 專區中尋找更多 用於 Linux 開發人員的參考資料。
- linux中的man page也是重要的資訊來源。
http://www.ibm.com/developerworks/cn/linux/l-time/
相關文章
- Linux下Shell的計時Linux
- linux和solaris下的時間計算Linux
- Lazarus在Win和Linux下的計時Linux
- LINUX C系統程式設計與PYTHON中的時間模組對比Linux程式設計Python
- Linux kernel 的設計是否已經過時?Linux
- Flutter 中“倒數計時”的那些事兒Flutter
- Linux雲端計算中的高頻命令Linux
- Zeit – Linux圖形化中定時任務的工具Linux
- Linux 中實時檢視日誌的3種方法Linux
- vue當中設計Tabbar外掛時的思考VuetabBar
- php中計算時間差的幾種方法PHP
- 網路程式設計中的超時檢測程式設計
- Linux計算一天前的日期,一個小時前的時間等Linux
- linux中crontab計劃任務怎麼刪除?linux中crontab計劃任務的刪除方法Linux
- 在 Linux 中以互動方式實時檢視Apache Web訪問統計LinuxApacheWeb
- Linux系統中延時任務及定時任務Linux
- Linux中如何實現定時任務Linux
- Android中handler倒數計時Android
- Linux Shell 時間運算以及時間差計算方法Linux
- Linux使用adsl計時制分享(轉)Linux
- Shell指令碼——Linux系統中的時間猜數字指令碼Linux
- Android 中如何計算 App 的啟動時間?AndroidAPP
- Android中如何計算App的啟動時間?AndroidAPP
- 趣圖:“大學時學習中的程式設計”與“實際工作專案中的程式設計”程式設計
- linux網路程式設計中的errno處理Linux程式設計
- Linux 程式設計中的檔案鎖之 flockLinux程式設計
- Linux 程式設計之Bash中的變數(轉)Linux程式設計變數
- 如何在Linux中啟動時新增服務Linux
- Linux系統中檔案時間常用的三種型別!Linux型別
- Linux程式設計(獲取系統時間)Linux程式設計
- Linux Shell程式設計(22)——時間/日期 命令Linux程式設計
- C#中計算兩個時間的差,得到月份C#
- Linux 中如何設定每個特定的時間執行特定的程式Linux
- Linux下關於時間概念的C語言程式設計LinuxC語言程式設計
- Flutter倒數計時/計時器的實現Flutter
- 如何在 Linux 下統計高速網路中的流量Linux
- 如何在 Ubuntu Linux 中設定或更改時區UbuntuLinux
- 在Linux中,如何實時監控網路流量?Linux