學習PHP中的高精度計時器HRTime擴展 https://www.cnblogs.com/chezxiaoqiang/archive/2012/03/23/2674386.html

不知道大家還記得在學校的時候體育測試時老師帶的秒錶嗎?當槍聲想起時,我們開始跑步,這時秒錶啓動,當我們跑過終點後,老師會按下按扭記錄我們的成績,這就是一個典型的定時器的應用。今天我們要學習的內容其實就是和這個體育測驗的秒錶類似的一個功能擴展,它就是 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 。中間的那一段沒有在定時器中的循環代碼沒有計入到總的計時時間中。

總結

是不是很有意思,它的作用真的和我們的體育老師所用的那個秒錶一模一樣,老師們的秒錶也都是可以按多次記錄第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

https://www.cnblogs.com/chezxiaoqiang/archive/2012/03/23/2674386.html

各自媒體平臺均可搜索【硬核項目經理】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章