#不知道大家還記得在學校的時候體育測試時老師帶的秒錶嗎?當槍聲想起時,我們開始跑步,這時秒錶啟動,當我們跑過終點後,老師會按下按扭記錄我們的成績,這就是一個典型的定時器的應用。今天我們要學習的內容其實就是和這個體育測驗的秒錶類似的一個功能擴展,它就是 PHP 的 HRTime 擴展。
時鐘拍子
首先我們要了解什麼叫做系統的時鐘拍子。當 Linux 系統啟動之後,會同時啟動一個時鐘節拍器,以納秒為單位進行計時,而我們的 HRTime 擴展的真實名稱是 高精度時間 擴展。也就是說,它正是基於作業系統的時鐘節拍器,能夠以奈秒為單位進行計時。
1秒=1000毫秒=1000000微妙=1000000000奈秒,這是秒、毫秒、微秒和奈秒的關係,看出來它的精度有多高了吧。 1秒等於10億納秒,這樣我們就可以得到一個非常精確的時間間隔計數。
HRTime 擴充功能直接在 PECL 進行下載安裝就可以了,和其他的普通擴充功能沒有什麼差別。
取得系統時脈節拍資訊 Ticks
我們先來看看如何取得作業系統的時鐘節拍,也就是這個 Ticks 。關於它的內容在學習作業系統的時候相信已經有不少的同學接觸過了,這裡我們來看看使用 HRTime 擴充功能如何取得。
print_r(hrtime()); // Array // ( // [0] => 3758 // [1] => 407409171 // ) echo hrtime(true), PHP_EOL; // 3758407428932
hrtime() 這個函數在 PHP7 之後已經整合在預設 PHP 環境中了。它不需要 HRTime 擴充功能就可以使用。這個函數在沒有參數的情況下回傳的是一個數組,第 0 項是系統啟動到現在的秒數,第 1 項就是對應的奈秒數。如果給它的參數設定一個 true 的話,它將直接傳回將秒和奈秒拼接起來的實際奈秒時間戳記。
echo HRTime\PerformanceCounter::getFrequency(), PHP_EOL; // 1000000000 echo HRTime\PerformanceCounter::getTicks(), PHP_EOL; // 3758428256236 echo HRTime\PerformanceCounter::getTicksSince(1212), PHP_EOL; // 3758428257494 $a = HRTime\PerformanceCounter::getTicks(); echo HRTime\PerformanceCounter::getTicksSince($a), PHP_EOL; // 412
接下來的這三個函數就是 HRTime 擴充中的 PerformanceCounter 物件的靜態函數了。 PerformanceCounter 物件的意思是效能計數器,getFrequency() 表示的是計時器頻率(以滴答Ticks/秒為單位),可以看出,它回傳的就是奈秒單位,也就是 10億 。 getTicks() 回傳的是目前的時脈節拍時間,可以看出它和 hrtime(true) 函數的結果是一樣的,都是傳回的系統啟動後的時脈節拍時間。 getTicksSince() 方法則是根據指定的奈秒數回傳時間間隔,類似 date_diff() 的感覺,其實就像我們的 time() - time() 這樣的運算。透過這個方法就可以獲得一段程式碼兩次運行的時間間隔,而且是以納秒為單位哦。
定時器功能
接下來就是我們文章的重點內容了,也就是定時器功能的實作。上面已經說過,使用 getTickSince() 其實也能做到監控一段程式碼的運行時間間隔,不過以下將學習到的內容將會更加強大。
$c = new HRTime\StopWatch; $c->start(); for ($i = 0; $i < 1024*1024; $i++); echo 'isRunning: ', $c->isRunning(), PHP_EOL; // isRunning: 1 $c->stop(); echo 'Time NS: ', $c->getLastElapsedTime(HRTime\Unit::NANOSECOND), PHP_EOL; echo 'Time US: ', $c->getLastElapsedTime(HRTime\Unit::MICROSECOND), PHP_EOL; echo 'Time MS: ', $c->getLastElapsedTime(HRTime\Unit::MILLISECOND), PHP_EOL; echo 'Time S: ', $c->getLastElapsedTime(HRTime\Unit::SECOND), PHP_EOL; // Time NS: 6929888 // Time US: 6929.888 // Time MS: 6.929888 // Time S: 0.006929888 echo 'Ticks: ',$c->getLastElapsedTicks(), PHP_EOL; // Ticks: 6929888 echo 'isRunning: ',$c->isRunning(), PHP_EOL; //
我們需要實例化一個 StopWatch 對象,然後呼叫它的 start() 方法,這樣一個定時器就啟動了。 StopWatch 的英文涵義本身就是定時器的意思,所以這個物件是專門為定時器的操作所服務的。透過 isRunning() 方法我們可以判斷目前定時器是否運行,其實就是判斷目前是否是在一個 start() 方法之後,如果不在 start() 和 stop() 範圍中,那麼它將傳回 false 。在測試程式碼中,我們執行一個 1024*1024 的空循環,然後再使用 stop() 方法結束計時器。
從程式碼可以看出,getLastElapsedTime() 就是要取得我們上面的那個start() 到stop() 之間的程式碼運行耗時的時間間隔訊息,它的參數可以指定為秒、毫秒、微秒、奈秒。本身這個方法的意思就是要取得取得最後一個間隔的運行時間。 getLastElapsedTicks() 則是獲得最後一次間隔的時脈節拍資訊。既然有【最後一次】這四個字,那麼也就說明這個物件是可以多次呼叫的來分段計時的。並且,它還是可以將多段不同的計時進行匯總,獲得全部的時間間隔資訊的。
// 不在计时范围内 for ($i = 0; $i < 1024*1024; $i++); $c->start(); for ($i = 0; $i < 1024*1024; $i++); $c->stop(); echo 'Time NS: ', $c->getLastElapsedTime(HRTime\Unit::NANOSECOND), PHP_EOL; echo 'Time US: ', $c->getLastElapsedTime(HRTime\Unit::MICROSECOND), PHP_EOL; echo 'Time MS: ', $c->getLastElapsedTime(HRTime\Unit::MILLISECOND), PHP_EOL; echo 'Time S: ', $c->getLastElapsedTime(HRTime\Unit::SECOND), PHP_EOL; // Time NS: 7154010 // Time US: 7154.01 // Time MS: 7.15401 // Time S: 0.00715401 echo 'All Time NS: ', $c->getElapsedTime(HRTime\Unit::NANOSECOND), PHP_EOL; echo 'All Time US: ', $c->getElapsedTime(HRTime\Unit::MICROSECOND), PHP_EOL; echo 'All Time MS: ', $c->getElapsedTime(HRTime\Unit::MILLISECOND), PHP_EOL; echo 'All Time S: ', $c->getElapsedTime(HRTime\Unit::SECOND), PHP_EOL; // All Time NS: 14083898 // All Time US: 14083.898 // All Time MS: 14.083898 // All Time S: 0.014083898 echo 'All Ticks: ', $c->getElapsedTicks(), PHP_EOL; // All Ticks: 14083898
在這段程式碼中,我們在兩段計時測試程式碼中插入了一個循環測試程式碼,它不會計入計時資料。接著,我們重新start() 開始一個新的計時,在最後,我們透過getElapsedTime() 和getElapsedTicks() 兩個方法獲得總的計時時間,可以看出上面的6929888 加上這次的7154010 結果正好是14083898 。中間的那一段沒有在定時器中的循環程式碼沒有計入到總的計時時間。
推薦學習:《PHP影片教學》
总结
是不是很有意思,它的作用真的和我们的体育老师所用的那个秒表一模一样,老师们的秒表也都是可以按多次记录第1名到最后1名的全部跑步成绩,并且最后还有一个总的时间,而在代码中我们也是完全相似的操作。这个扩展对于精细的性能调试非常有用,而且也能够针对一些需要这种高精度时间差的业务进行相关的开发。
测试代码: https://github.com/zhangyue0503/dev-blog/blob/master/php/202010/source/3.学习PHP中的高精度计时器HRTime扩展.php 参考文档: https://www.php.net/manual/zh/book.hrtime.php