The problem of slow execution of PHP uniqid function

巴扎黑
Release: 2016-11-21 10:23:58
Original
1171 people have browsed it

A certain requirement some time ago: Customers submit a simple form to create an H5 page for scratch card activities that is suitable for all terminals (PC, Pad, Phone), which involves the function that customers can generate limit 6W prize codes online.
Because we need to keep each event’s prize code unique, we first prepare to use PHP’s uniqid function to generate a UUID (Universally Unique IDentifier, also called GUID, a globally unique identifier, a unique identifier generated by an algorithm) to generate .
But when we used to generate 1W test, we found that it took dozens of seconds to generate some data, not including the time to insert into the database. Then we wrote a simple example with xhprof for performance testing

<?php
xhprof_enable(XHPROF_FLAGS_CPU|XHPROF_FLAGS_MEMORY);
function   myfunc(){
    for($i=0;$i<10000;$i++){
        $data = uniqid();
    }
}
myfunc();
$data = xhprof_disable();
print_r($data);
Copy after login

Test result:

[myfunc==>uniqid] => Array(
            [ct] => 10000
            [wt] => 39975062
            [cpu] => 0
            [mu] => 960752
            [pmu] => 0
)
Copy after login

It actually needs It takes nearly 40 seconds to generate, and a single execution takes 3969 microseconds, which is 0.003969 seconds to generate. If the user submits the form and generates a redemption code at the same time, it will take 4 minutes to respond to the user in the worst case. Of course, it can be generated asynchronously using the message queue, but why does uniqid take so much time to generate a simple string?

Then check the implementation source code of uniqid, the code is posted below

PHP_FUNCTION(uniqid)
{
     char *prefix = "";
#if defined(__CYGWIN__)
     zend_bool more_entropy = 1;
#else
     zend_bool more_entropy = 0;
#endif
     char *uniqid;
     int sec, usec, prefix_len = 0;
     struct timeval tv;
     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sb", &prefix, &prefix_len,
                                     &more_entropy)) {
          return;
     }
#if HAVE_USLEEP && !defined(PHP_WIN32)
     if (!more_entropy) {
#if defined(__CYGWIN__)
          php_error_docref(NULL TSRMLS_CC, E_WARNING, "You must use &#39;more entropy&#39; under CYGWIN");
          RETURN_FALSE;
#else
          usleep(1);
#endif
     }
#endif
     gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);
     sec = (int) tv.tv_sec;
     usec = (int) (tv.tv_usec % 0x100000);
     /* The max value usec can have is 0xF423F, so we use only five hex
     * digits for usecs.
     */
     if (more_entropy) {
          spprintf(&uniqid, 0, "%s%08x%05x%.8F", prefix, sec, usec, php_combined_lcg(TSRMLS_C) * 10);
     } else {
          spprintf(&uniqid, 0, "%s%08x%05x", prefix, sec, usec);
     }
     RETURN_STRING(uniqid, 0);
}
Copy after login

Looking at the logic, there are no complicated operations. It also simply processes the current time seconds and microseconds, and then writes a simple test

int getUniqid( char * uid) {
     int sec, usec;
     struct timeval tv;
     gettimeofday(( struct timeval *) &tv, ( struct timezone *) NULL);
     sec = ( int) tv. tv_sec;
     usec = ( int ) (tv.tv_usec % 0x100000);
     sprintf(uid, "%08x%05x" , sec, usec);
     return 1;
}
Copy after login

Execute 1W It only takes 2000 microseconds. Why is this? However, we found that there are a lot of duplications in the generated uid, so we paid attention to the usleep function in the original code.
Added the usleep function in the test, this time it took nearly 40 seconds to match the PHP result. usleep is here to keep every The uid generated this time is different.
The problem occurred in the usleep function, and then add the interval time before and after usleep. The code is as follows

struct timeval start, end;
      gettimeofday(( struct timeval *) &start, ( struct timezone *) NULL);
      usleep(1);
      gettimeofday(( struct timeval *) &end, ( struct timezone *) NULL);
      unsigned long space = (end.tv_sec - start. tv_sec) * 1000000 + end.tv_usec
              - start. tv_usec;
       spaceCost += space;
Copy after login

Finally, it was found that it takes 39.99587739.995877 seconds to generate the 1W prize code, and the total usleep interval time is 39.982442m. It was found by printing the usleep time Each time usleep(1) takes 4000 microseconds from process suspension to wake-up, we already know that usleep cannot achieve the accuracy, and the difference is too far.
Finally, I used the following code to generate the prize code

/**
     * 生成兑换码并保存到数据库  返回setNo
     * $pageId 活动ID
     * $level 奖品等级
     * $numbers 生成奖品的个数
     */
    public static  function generateCDKEYAndSave($pageId,$level,$numbers){
        $level1Prefix =array(2,5,9,&#39;E&#39;,&#39;F&#39;,&#39;M&#39;,&#39;N&#39;,&#39;Q&#39;,&#39;K&#39;,&#39;Z&#39;);//一等奖的前缀
        $level2Prefix =array(1,3,7,&#39;A&#39;,&#39;C&#39;,&#39;J&#39;,&#39;R&#39;,&#39;U&#39;,&#39;V&#39;,&#39;X&#39;);//二等奖的前置
        $level3Prefix = array(4,6,8,&#39;B&#39;,&#39;D&#39;,&#39;G&#39;,&#39;H&#39;,&#39;I&#39;,&#39;L&#39;,&#39;O&#39;,&#39;P&#39;,&#39;R&#39;,&#39;S&#39;,&#39;T&#39;,&#39;W&#39;,&#39;Y&#39;);//三等奖的前缀
        if(empty($pageId) || empty($level) || empty($numbers)) return false;
        $levelPrefix =$level1Prefix;
        if($level==2) $levelPrefix = $level2Prefix;
        if($level==3) $levelPrefix = $level3Prefix;
        $codes =array();
        $now = time();
        for($i=0;$i<$numbers;$i++){
            $prefixKey = array_rand($levelPrefix);
            $prefix = self::COUPON_PREFIX.$levelPrefix[$prefixKey];
            //$code =base_convert(hexdec(md5(uniqid())),10,26); 服务器上面uniqid执行慢的要死
            //$code =base_convert(hexdec(md5($pageId.&#39;A#1$v&&#39;.$i)),10,26);//数据过多 hexdec丢失大量精度
            $code1 = base_convert(substr(md5($pageId.$i.$now), 0, 10), 16, 36);
            $code2 = base_convert($i, 10, 26);
            $code2Len = strlen($code2);
            if ($code2Len == 1) {
                $code2 .= chr(rand(82, 90)) . chr(rand(82, 90)) . chr(rand(82, 90));
            }
            else if ($code2Len == 2) {
                $code2 .= chr(rand(82, 90)) . chr(rand(82, 90));
            }
            else if ($code2Len == 3) {
                $code2 .= chr(rand(82, 90));
            }
            $code =$code1.$code2;
            $codes[] = $prefix.strtoupper($code);
        }
       return $codes;
    }
Copy after login

Comes with uuid test code

#include <stdio.h>
#include <malloc.h>
#include <sys/time.h>
#include <unistd.h>
unsigned long sleepCost = 0;
int getUniqid( char * uid,int times) {
      struct timeval start, end;
      gettimeofday(( struct timeval *) &start, ( struct timezone *) NULL);
      usleep(1);
      gettimeofday(( struct timeval *) &end, ( struct timezone *) NULL);
      unsigned long space = (end.tv_sec - start. tv_sec) * 1000000 + end.tv_usec
              - start. tv_usec;
     sleepCost += space;
      if (0 == times%1000) printf ("\n-----sleep cost-------\n%lu usec\n", space);
      int sec, usec;
      struct timeval tv;
      gettimeofday(( struct timeval *) &tv, ( struct timezone *) NULL);
     sec = ( int) tv. tv_sec;
     usec = ( int ) (tv.tv_usec % 0x100000);
      sprintf(uid, "%08x%05x" , sec, usec);
      return 1;
}
int main( int argc, char * argv[]) {
      struct timeval start, end;
      gettimeofday(( struct timeval *) &start, ( struct timezone *) NULL);
      for ( int i = 1; i <= 10000; i++) {
           char data[20];
          getUniqid(data,i);
     }
      gettimeofday(( struct timeval *) &end, ( struct timezone *) NULL);
      unsigned long space = (end.tv_sec - start. tv_sec) * 1000000 + end.tv_usec
              - start. tv_usec;
      printf( "\n-----cost-------\n% lu usec\n  \n-----sum sleep sost-------\n% lu usec\n" , space,sleepCost);
}
Copy after login


Related labels:
php
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!