diff options
author | Emmanuel Dreyfus <manu@netbsd.org> | 2017-07-19 04:36:55 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2017-07-19 19:54:19 +0200 |
commit | d25049cc1b74ae445d6521997a421a7462f1ad5b (patch) | |
tree | 04af1a5b08d8616857946a114e84f30883416d3a | |
parent | 6fcc7134f1d4aaf60e24202ed316c61636c5a95d (diff) | |
download | php-git-d25049cc1b74ae445d6521997a421a7462f1ad5b.tar.gz |
Fixed bug #74851: Improve uniqid() performance
uniqid() relies on microsecond-precise system clock to produce an
unique identifier. In order to avoid using the same value, the
original implementation calls usleep(1) to wait for the next microsecond.
Unfortunately, usleep() specification says "The suspension time may be
longer than requested due to the scheduling of other activity by the
system." Indeed, the pause may as as long as an entire execution slice,
causing a uniqid() call to last more than 10 ms.
This is fixed by replacing the usleep() call by time polling using
gettimeofday() until the microsecond changes. Since the getttimeoday()
system call lasts around a microsecond, only a small time is wasted
calling multiple gettimeofday. On the benefit side, uniqid() performance
in increased 10000 fold without changing its behavior.
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | ext/standard/uniqid.c | 29 |
2 files changed, 19 insertions, 14 deletions
@@ -11,6 +11,10 @@ PHP NEWS - SPL: . Fixed bug #74669 (Unserialize ArrayIterator broken). (Andrew Nester) +- Standard: + . Fixed bug #74851 (uniqid() without more_entropy performs badly). + (Emmanuel Dreyfus) + 20 Jul 2017, PHP 7.2.0beta1 - Core: diff --git a/ext/standard/uniqid.c b/ext/standard/uniqid.c index 22173ae01d..e8f2f19520 100644 --- a/ext/standard/uniqid.c +++ b/ext/standard/uniqid.c @@ -38,17 +38,15 @@ #include "php_lcg.h" #include "uniqid.h" +#ifdef HAVE_GETTIMEOFDAY +ZEND_TLS struct timeval prev_tv = { 0, 0 }; + /* {{{ proto string uniqid([string prefix [, bool more_entropy]]) Generates a unique ID */ -#ifdef HAVE_GETTIMEOFDAY PHP_FUNCTION(uniqid) { char *prefix = ""; -#if defined(__CYGWIN__) - zend_bool more_entropy = 1; -#else zend_bool more_entropy = 0; -#endif zend_string *uniqid; int sec, usec; size_t prefix_len = 0; @@ -60,17 +58,20 @@ PHP_FUNCTION(uniqid) Z_PARAM_BOOL(more_entropy) ZEND_PARSE_PARAMETERS_END(); -#if HAVE_USLEEP && !defined(PHP_WIN32) if (!more_entropy) { -#if defined(__CYGWIN__) - php_error_docref(NULL, E_WARNING, "You must use 'more entropy' under CYGWIN"); - RETURN_FALSE; -#else - usleep(1); -#endif + /* This implementation needs current microsecond to change, + * hence we poll time until it does. This is much faster than + * calling usleep(1) which may cause the kernel to schedule + * another process, causing a pause of around 10ms. + */ + do { + (void)gettimeofday((struct timeval *) &tv, (struct timezone *) NULL); + } while (tv.tv_sec == prev_tv.tv_sec && tv.tv_usec == prev_tv.tv_usec); + + prev_tv.tv_sec = tv.tv_sec; + prev_tv.tv_usec = tv.tv_usec; } -#endif - gettimeofday((struct timeval *) &tv, (struct timezone *) NULL); + sec = (int) tv.tv_sec; usec = (int) (tv.tv_usec % 0x100000); |