TIL Why is uniqid() slow?
Jump to: TL;DR
While profiling RulerZ with
Blackfire.io, I noticed that a non-negligible amount of
time was consumed by PHP’s uniqid()
function.
For those who don’t know, this function provides an easy way to generate unique identifiers based on the current date and an optional prefix.
Wait, the “current date” part can actually be interesting for our performance issue. What happens if we write something like this:
1foreach ($foo as $bar) {
2 $awesomeCollection[] = new CoolObject(uniqid(), $bar);
3}
A simple loop like this should run pretty fast, so if uniqid()
simply relies
on the current date we should have collisions… but it is not the case. Why?
The answer to this question lies in the implementation of
uniqid()
:
in order to avoid time-induced collisions on the same host, uniqid()
literally
waits before getting the current date. That’s why there is a usleep(1)
call
in the function’s implementation.
But wait, there’s more. As the man page for usleep
says, “The usleep()
function suspends execution of the calling thread for (at least) usec
microseconds. The sleep may be lengthened slightly by any system activity or by
the time spent processing the call or by the granularity of system timers.”.
Yup, if you’re not lucky usleep(n)
calls can suspend the execution for more
than n microseconds.
Now, you may have noticed that the usleep()
call is conditioned by the
more_entropy
variable, which happens to be a parameter exposed in the user-land
uniqid
.
By setting more_entropy
to true
, we choose to provide an additional source
of entropy by appending data using php_combined_lcg()
(a pseudo-random number
generator), thus preventing time-induced collisions without having to sleep.
So if you use uniqid()
, make sure that the $more_entropy
parameter is set
to true
.
TL;DR
If called without$more_entropy = true
, uniqid()
calls sleep for at least 1 microsecond. uniqid($prefix, $more_entropy = true)
will prevent function from sleeping.