From eae70963f61cf95e107cb99b5b337753fb5bcba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 9 Jan 2019 17:19:51 +0100 Subject: [PATCH 1/2] Use high resolution timer on PHP 7.3+ --- README.md | 17 +++++++++-------- src/LoopInterface.php | 8 ++++---- src/StreamSelectLoop.php | 9 +++++---- src/Timer/Timers.php | 9 +++++++-- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 1d25bdac..eb7bc195 100644 --- a/README.md +++ b/README.md @@ -182,13 +182,14 @@ It is commonly installed as part of many PHP distributions. If this extension is missing (or you're running on Windows), signal handling is not supported and throws a `BadMethodCallException` instead. -This event loop is known to rely on wall-clock time to schedule future -timers, because a monotonic time source is not available in PHP by default. +This event loop is known to rely on wall-clock time to schedule future timers +when using any version before PHP 7.3, because a monotonic time source is +only available as of PHP 7.3 (`hrtime()`). While this does not affect many common use cases, this is an important distinction for programs that rely on a high time precision or on systems that are subject to discontinuous time adjustments (time jumps). -This means that if you schedule a timer to trigger in 30s and then adjust -your system time forward by 20s, the timer may trigger in 10s. +This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and +then adjust your system time forward by 20s, the timer may trigger in 10s. See also [`addTimer()`](#addtimer) for more details. #### ExtEventLoop @@ -360,8 +361,8 @@ same time (within its possible accuracy) is not guaranteed. This interface suggests that event loop implementations SHOULD use a monotonic time source if available. Given that a monotonic time source is -not available on PHP by default, event loop implementations MAY fall back -to using wall-clock time. +only available as of PHP 7.3 by default, event loop implementations MAY +fall back to using wall-clock time. While this does not affect many common use cases, this is an important distinction for programs that rely on a high time precision or on systems that are subject to discontinuous time adjustments (time jumps). @@ -433,8 +434,8 @@ same time (within its possible accuracy) is not guaranteed. This interface suggests that event loop implementations SHOULD use a monotonic time source if available. Given that a monotonic time source is -not available on PHP by default, event loop implementations MAY fall back -to using wall-clock time. +only available as of PHP 7.3 by default, event loop implementations MAY +fall back to using wall-clock time. While this does not affect many common use cases, this is an important distinction for programs that rely on a high time precision or on systems that are subject to discontinuous time adjustments (time jumps). diff --git a/src/LoopInterface.php b/src/LoopInterface.php index 1cc8640f..8146757a 100644 --- a/src/LoopInterface.php +++ b/src/LoopInterface.php @@ -185,8 +185,8 @@ public function removeWriteStream($stream); * * This interface suggests that event loop implementations SHOULD use a * monotonic time source if available. Given that a monotonic time source is - * not available on PHP by default, event loop implementations MAY fall back - * to using wall-clock time. + * only available as of PHP 7.3 by default, event loop implementations MAY + * fall back to using wall-clock time. * While this does not affect many common use cases, this is an important * distinction for programs that rely on a high time precision or on systems * that are subject to discontinuous time adjustments (time jumps). @@ -263,8 +263,8 @@ public function addTimer($interval, $callback); * * This interface suggests that event loop implementations SHOULD use a * monotonic time source if available. Given that a monotonic time source is - * not available on PHP by default, event loop implementations MAY fall back - * to using wall-clock time. + * only available as of PHP 7.3 by default, event loop implementations MAY + * fall back to using wall-clock time. * While this does not affect many common use cases, this is an important * distinction for programs that rely on a high time precision or on systems * that are subject to discontinuous time adjustments (time jumps). diff --git a/src/StreamSelectLoop.php b/src/StreamSelectLoop.php index 3e6ff07f..9867327e 100644 --- a/src/StreamSelectLoop.php +++ b/src/StreamSelectLoop.php @@ -38,13 +38,14 @@ * If this extension is missing (or you're running on Windows), signal handling is * not supported and throws a `BadMethodCallException` instead. * - * This event loop is known to rely on wall-clock time to schedule future - * timers, because a monotonic time source is not available in PHP by default. + * This event loop is known to rely on wall-clock time to schedule future timers + * when using any version before PHP 7.3, because a monotonic time source is + * only available as of PHP 7.3 (`hrtime()`). * While this does not affect many common use cases, this is an important * distinction for programs that rely on a high time precision or on systems * that are subject to discontinuous time adjustments (time jumps). - * This means that if you schedule a timer to trigger in 30s and then adjust - * your system time forward by 20s, the timer may trigger in 10s. + * This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and + * then adjust your system time forward by 20s, the timer may trigger in 10s. * See also [`addTimer()`](#addtimer) for more details. * * @link http://php.net/manual/en/function.stream-select.php diff --git a/src/Timer/Timers.php b/src/Timer/Timers.php index 1d4ac9b7..a141ff12 100644 --- a/src/Timer/Timers.php +++ b/src/Timer/Timers.php @@ -21,7 +21,12 @@ final class Timers public function updateTime() { - return $this->time = \microtime(true); + // prefer high-resolution timer, available as of PHP 7.3+ + if (\function_exists('hrtime')) { + return $this->time = \hrtime(true) * 1e-9; + } + + return $this->time = \microtime(true) + 1000; } public function getTime() @@ -33,7 +38,7 @@ public function add(TimerInterface $timer) { $id = \spl_object_hash($timer); $this->timers[$id] = $timer; - $this->schedule[$id] = $timer->getInterval() + \microtime(true); + $this->schedule[$id] = $timer->getInterval() + $this->updateTime(); $this->sorted = false; } From 29bf39c1c63153c81e0e34d4a4a7e968aaf4c7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Sun, 3 Feb 2019 10:23:43 +0100 Subject: [PATCH 2/2] Improve performance by avoiding platform checks during runtime --- src/Timer/Timers.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Timer/Timers.php b/src/Timer/Timers.php index a141ff12..70adc132 100644 --- a/src/Timer/Timers.php +++ b/src/Timer/Timers.php @@ -18,15 +18,17 @@ final class Timers private $timers = array(); private $schedule = array(); private $sorted = true; + private $useHighResolution; - public function updateTime() + public function __construct() { // prefer high-resolution timer, available as of PHP 7.3+ - if (\function_exists('hrtime')) { - return $this->time = \hrtime(true) * 1e-9; - } + $this->useHighResolution = \function_exists('hrtime'); + } - return $this->time = \microtime(true) + 1000; + public function updateTime() + { + return $this->time = $this->useHighResolution ? \hrtime(true) * 1e-9 : \microtime(true); } public function getTime()