2022年11月17日

PHP 如何測量程式執行時間 - 使用 time、microtime 及 hrtime

本篇為 PHP 系列

一支程式的效能有幾個重要的指標,其中一個是記憶體用量,之前的文章中有介紹過如何衡量記憶體用量,而今天這一篇文章要介紹的則是另一個重要指標:執行時間。

要如何測量程式的執行時間?其實原理很簡單,只要記錄開始時間結束時間,然後以後者減去前者就好了。

在 PHP 中有 time()microtimehrtime 可以用來取得目前時間,而這三者的主要差別在於時間的精細度。

time()

time() 回傳的是當下的 Unix timestamp,單位是秒。Unix timestamp,其代表的數字是 1970/01/01 00:00:00 GMT+0 到目前為止所累計的秒數。例如:

print(time()); // 輸出 : 1668667291

因此要計算某段程式的執行時間可以這麼做:

$startTime = time(); // 開始時間 for($i = 0; $i < 100000; $i++){ $i++; } $endTime = time(); // 結束時間 print($endTime - $startTime); // 0

如上面的的例子,因為簡單的加法對 PHP 來說是非常快速的運算(其實對任何語言應該都是吧),所以跑 100,000 次所需的時間不到 1 秒,因此結果是 0 秒。如果我們讓次數增加到 1,000,000,000 次:

$startTime = time(); // 開始時間 for($i = 0; $i < 1000000000; $i++){ $i++; } $endTime = time(); // 結束時間 print($endTime - $startTime); // 輸出: 1

現在我們看到結果是 1 秒,可以看到簡單的加法跑了十億次才花了 1 秒多。

看到這邊我們會發現一個小問題,很多程式其實並不會執行超過一秒,如果都使用 time() 來測量的話結果都會是 0,那就比較不出差異了,總不能每次都讓他們跑一千次吧?因此只能用來計算時間是不夠用的,精密度不夠,也就是因為這樣,其實大部分的時候我會使用 microtime()來做測量。

microtime()

microtime() 的一樣回傳的是 Unix Timestamp,但精準度會來到微秒,如果不知道微秒的大小,這邊稍微科普一下,微秒是 10^-6 秒,也就是 0.000001 秒,而毫秒則是 10^-3 秒,也就是 0.001 秒。

microtime() 需要一個參數,這個參數決定是否回傳浮點數,預設是 fasle,會回傳字串,若傳 true 則會回傳浮點數。例如:

<?php var_dump(microtime(false)); // 輸出: string(21) "0.71466000 1668671090" var_dump(microtime(true)); // 輸出: float(1668671090.714438)

習慣上我們會帶 true 使用浮點數,這樣計算上比較方便。那可以看到上面的範例的回傳值,整數的部分仍然是秒數,小數點以下六位數則是準確到微秒。現在我們實際拿 microtime() 來進行測量:

<?php $startTime = microtime(true); // 開始時間 for($i = 0; $i < 100000; $i++){ $i++; } $endTime = microtime(true); // 結束時間 print(round($endTime - $startTime, 6)); // 輸出: 0.000305 (秒) print(PHP_EOL); print(round(($endTime - $startTime) * 1000000)); // 轉換成微秒,輸出: 305 (微秒)

這邊我們一樣用前面的簡單加法,跑了十萬次,這一次我們可以看到實際的執行時間了,以這次為例子,跑了 0.000305秒,也就是 305 微秒。

hrtime()

最後,如果我們不滿足於微秒的話,在 PHP 7.3 以後,提供了 hrtime(),它回傳的的不是 UNIX TIMESTAMP,而是系統的高解析度時間 (high resolution time),它的精準度來到了奈秒 (10^-9 秒)。

hrtime() 用法與 microtime() 類似,也需要一個參數,這個參數決定是否回傳數字,而這個回傳的這個數字本身就是奈秒,如果傳 false 則會回傳一個陣列,格式是 [秒, 奈秒]。一樣會習慣傳 true 方便計算。

<?php $startTimeMi = microtime(true); // micro 開始時間 $startTimeHr = hrtime(true); // hr 開始時間 for($i = 0; $i < 100000; $i++){ $i++; } $endTimeHr = hrtime(true); // hr 結束時間 $endTimeMi = microtime(true); // micro 結束時間 var_dump(round($endTimeMi - $startTimeMi, 6)); // 輸出: 0.000314 秒 var_dump($endTimeHr - $startTimeHr); // 輸出: 299984 奈秒

hrtime 除了精準度可以到達奈秒以外,根據定義,hrtime 可以取得的時間是更精準而不受其他因素影響的,因此非常適合拿來衡量程式的表現,PHP 官方其實也建議使用 hrtime() 來做效能衡量。個人目前是以 microtime 為主,未來也許會跟進採用 hrtime。

參考

PHP: time - Manual

PHP: microtime - Manual

PHP: hrtime - Manual

本篇為 PHP 系列