ZetCode

PHP hrtime 函数

上次修改时间:2025 年 5 月 18 日

PHP 中的 hrtime 函数提供高分辨率计时测量,以纳秒为单位返回系统的高分辨率时间。它在 PHP 7.3 中引入,非常适合需要微秒级精度可能不足的精确基准测试和性能测量。

PHP 中的 hrtime 函数提供高分辨率计时,具有纳秒精度,返回总纳秒计数(作为整数)或包含秒和纳秒的数组。它具有单调性,这意味着它不受系统时间变化的影响,使其非常适合性能基准测试、剖析和执行时间优化。为了提高测量精度,应通过多次迭代测试小型操作,同时考虑开销以确保精确的结果。为了更好的可读性,可以将值转换为常用的单位,例如毫秒或秒。

hrtime([ bool $get_as_number ]): mixed

hrtime 函数有一个可选的 $get_as_number 参数。如果设置为 true,则该函数以整数形式返回当前时间(以纳秒为单位)。如果设置为 false 或省略,则它返回一个包含两个元素的数组:秒和纳秒。

hrtime 的主要特征

hrtime 的基本用法

hrtime 可以通过两种方式调用:不带参数以获取当前时间,或者使用布尔参数以将时间作为数组获取。默认情况下,它将纳秒作为整数返回。

basic_usage.php
<?php 

declare(strict_types=1);

// Get current time in nanoseconds
$time = hrtime(true);
echo "Current time: $time nanoseconds\n";

// Get time as an array [seconds, nanoseconds]
$timeArray = hrtime();
print_r($timeArray);

// Measure time difference
$start = hrtime(true);
usleep(1000); // Sleep for 1000 microseconds (1ms)
$end = hrtime(true);

$duration = $end - $start;
echo "Slept for: " . ($duration / 1e6) . " milliseconds\n";

这演示了 hrtime 的基本用法。第一个调用以整数形式获取当前时间(以纳秒为单位)。第二个不带参数的调用返回一个包含秒和纳秒的数组。该示例还展示了如何通过计算两个 hrtime(true) 调用的差值来测量代码段的持续时间。

λ php basic_usage.php
Current time: 3846251417975 nanoseconds
Array
(
    [0] => 3846
    [1] => 251417975
)
Slept for: 1.067367 milliseconds

比较 hrtime 与 microtime

对于精确的计时测量,hrtime 提供了优于 microtime 的几个优势。此示例比较了这两个函数。

comparison.php
<?php 

declare(strict_types=1);

// Using microtime()
$startMicro = microtime(true);
usleep(100);
$endMicro = microtime(true);
$microDuration = ($endMicro - $startMicro) * 1e6; // Convert to microseconds

// Using hrtime()
$startHr = hrtime(true);
usleep(100);
$endHr = hrtime(true);
$hrDuration = ($endHr - $startHr) / 1e3; // Convert to microseconds

echo "microtime measured: $microDuration microseconds\n";
echo "hrtime measured: $hrDuration microseconds\n";

// Measure resolution
$iterations = 1000;
$times = [];

for ($i = 0; $i < $iterations; $i++) {

    $start = hrtime(true);
    $end = hrtime(true);
    $times[] = $end - $start;
}

echo "Minimum resolution: " . min($times) . " nanoseconds\n";
echo "Average resolution: " . array_sum($times)/$iterations . " nanoseconds\n";

这表明 hrtime 通常提供比 microtime 更高的分辨率计时。分辨率测试表明,hrtime 可以测量 microtime 可能无法准确捕捉的非常短的间隔。

λ php comparison.php
microtime measured: 16182.18421936 microseconds
hrtime measured: 803.9 microseconds
Minimum resolution: 0 nanoseconds
Average resolution: 63.9 nanoseconds

使用 hrtime 进行基准测试

hrtime 非常适合对代码段进行基准测试。以下是如何正确构建基准测试并计算有意义的统计数据。

benchmarking.php
<?php 

declare(strict_types=1);

function benchmark(callable $function, int $iterations = 1000): array
{
    $durations = [];
    
    // Warm-up (run once to avoid initialization bias)
    $function();
    
    for ($i = 0; $i < $iterations; $i++) {
        $start = hrtime(true);
        $function();
        $end = hrtime(true);
        $durations[] = $end - $start;
    }
    
    $total = array_sum($durations);
    
    return [
        'iterations' => $iterations,
        'total_ns' => $total,
        'avg_ns' => $total / $iterations,
        'min_ns' => min($durations),
        'max_ns' => max($durations),
        'variance' => stats_variance($durations),
    ];
}

function stats_variance(array $values): float
{
    $avg = array_sum($values) / count($values);
    $sum = 0;
    foreach ($values as $value) {
        $sum += ($value - $avg) ** 2;
    }
    return $sum / count($values);
}

// Example benchmark
$result = benchmark(function() {
    // Code to benchmark
    $array = range(1, 1000);
    $sum = array_sum($array);
}, 10000);

echo "Benchmark Results:\n";
echo "Iterations: {$result['iterations']}\n";
echo "Total time: " . ($result['total_ns'] / 1e9) . " seconds\n";
echo "Average: " . ($result['avg_ns'] / 1e6) . " ms\n";
echo "Min: {$result['min_ns']} ns\n";
echo "Max: {$result['max_ns']} ns\n";
echo "Variance: {$result['variance']}\n";

这演示了使用 hrtime 的稳健基准测试方法。基准测试函数多次运行代码,计算统计数据,并包括一个预热运行以考虑初始化成本。方差有助于识别计时的一致性。

λ php benchmarking.php
Benchmark Results:
Iterations: 10000
Total time: 0.070156 seconds
Average: 0.0070156 ms
Min: 6300 ns
Max: 63800 ns
Variance: 4937010.6400005

测量函数执行时间

hrtime 可以精确测量函数执行所需的时间。这是一个用于对任何可调用对象进行计时的可重用函数。

function_timing.php
<?php 

declare(strict_types=1);

function measure(callable $function, ...$args): array
{
    $start = hrtime(true);
    $result = $function(...$args);
    $end = hrtime(true);
    
    return [
        'result' => $result,
        'time_ns' => $end - $start,
    ];
}

// Example usage
function calculatePrimes(int $limit): array
{
    $primes = [];

    for ($i = 2; $i <= $limit; $i++) {

        $isPrime = true;
        for ($j = 2; $j <= sqrt($i); $j++) {
            if ($i % $j === 0) {
                $isPrime = false;
                break;
            }
        }

        if ($isPrime) {
            $primes[] = $i;
        }
    }

    return $primes;
}

$measurement = measure('calculatePrimes', 1000);
echo "Found " . count($measurement['result']) . " primes\n";
echo "Took " . ($measurement['time_ns'] / 1e6) . " milliseconds\n";

这展示了如何使用 hrtime 包装任何函数调用以进行精确计时。measure 函数返回结果和所花费的时间,允许您分析性能而不影响函数的操作。

λ php function_timing.php
Found 168 primes
Took 0.3902 milliseconds

真实世界的剖析示例

以下是如何使用 hrtime 来剖析算法的不同实现,以确定哪个更有效。

profiling.php
<?php 

declare(strict_types=1);

// Two implementations of Fibonacci sequence
function fibRecursive(int $n): int
{
    if ($n <= 1) return $n;
    return fibRecursive($n - 1) + fibRecursive($n - 2);
}

function fibIterative(int $n): int
{
    if ($n <= 1) return $n;
    
    $a = 0;
    $b = 1;
    
    for ($i = 2; $i <= $n; $i++) {
        $c = $a + $b;
        $a = $b;
        $b = $c;
    }
    
    return $b;
}

// Profile both implementations
function profile(string $name, callable $function, int $input): array
{
    $start = hrtime(true);
    $result = $function($input);
    $end = hrtime(true);
    
    return [
        'name' => $name,
        'result' => $result,
        'time_ns' => $end - $start,
    ];
}

$input = 30; // Warning: recursive gets very slow with larger numbers

$recursive = profile('Recursive', 'fibRecursive', $input);
$iterative = profile('Iterative', 'fibIterative', $input);

echo "Fibonacci($input) results:\n";
echo "{$recursive['name']}: {$recursive['result']} in " . 
     ($recursive['time_ns'] / 1e6) . " ms\n";
echo "{$iterative['name']}: {$iterative['result']} in " . 
     ($iterative['time_ns'] / 1e6) . " ms\n";

此示例清楚地显示了斐波那契数列的递归和迭代实现之间的性能差异,hrtime 提供了对时间差异的精确测量。

λ php profiling.php
Fibonacci(30) results:
Recursive: 832040 in 124.234367 ms
Iterative: 832040 in 0.005367 ms

最佳实践

要使用 hrtime 获得准确且有意义的结果,请遵循以下最佳实践

best_practices.php
<?php 

declare(strict_types=1);

// 1. Always measure multiple iterations for small operations
function measureMany(callable $function, int $iterations = 1000): float
{
    $start = hrtime(true);
    for ($i = 0; $i < $iterations; $i++) {
        $function();
    }

    $end = hrtime(true);
    return ($end - $start) / $iterations;
}

// 2. Account for the measurement overhead
$start = hrtime(true);
$end = hrtime(true);
$overhead = $end - $start;
echo "Measurement overhead: $overhead ns\n";

// 3. Use monotonic time for reliable measurements
// hrtime() is monotonic by default (unlike microtime())

// 4. Convert units appropriately for readability
function formatDuration(int $ns): string
{
    if ($ns < 1e3) return "$ns ns";
    if ($ns < 1e6) return round($ns/1e3, 2) . " μs";
    if ($ns < 1e9) return round($ns/1e6, 2) . " ms";
    return round($ns/1e9, 2) . " s";
}

// 5. Run warm-up iterations before measuring
function warmup(callable $function, int $iterations = 10): void
{
    for ($i = 0; $i < $iterations; $i++) {
        $function();
    }
}

// 6. Consider using a benchmarking library for complex scenarios
// Example: https://github.com/phpbench/phpbench

// 7. Document your measurement methodology
/**
 * Measures database query performance
 * @param callable $query Should execute and return the query result
 * @param int $iterations Number of times to run for stable measurement
 * @return array Contains 'result', 'time_ns', and 'iterations'
 */
function measureQuery(callable $query, int $iterations = 100): array
{
    warmup($query);
    
    $start = hrtime(true);
    $result = null;
    for ($i = 0; $i < $iterations; $i++) {
        $result = $query();
    }
    $end = hrtime(true);
    
    return [
        'result' => $result,
        'time_ns' => ($end - $start) / $iterations,
        'iterations' => $iterations
    ];
}

这些实践有助于确保您的计时测量准确、可靠且有意义。关键点是测量多次迭代,考虑开销,并正确记录您的方法。

在本文中,我们介绍了在 PHP 中使用 hrtime 的基础知识,包括如何测量执行时间,将其与 microtime 进行比较,以及将其用于基准测试。我们还讨论了用于精确测量和剖析实际场景的最佳实践。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我撰写了 1,400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出所有 PHP 教程。