PHP uniqid function Execution Slow problem
Previous time a requirement: a customer submits a simple form to create a scratch card activity H5 page that adapts to the full terminal (Pc,pad,phone), which involves the ability of a customer to generate a quota 6W prize code online.
Since it is necessary to maintain the unique prize code for each activity, we are first prepared to use PHP's uniqid function to generate the UUID (universally unique IDentifier, also known as a GUID, a globally unique identifier, which is a unique identifier generated by the algorithm).
But when we used the build 1W test, we found that it took dozens of seconds to generate, not including the time to insert into the database, and then wrote a simple example with xhprof for performance testing
Test results:
[Myfunc==>uniqid] = = Array ( [ct] = 10000 [WT] + 39975062 [CPU] = 0 [mu] = 960752< C5/>[PMU] = 0)
It takes nearly 40 seconds to generate, and a single execution takes 3969 microseconds, or 0.003969 seconds to generate. If the user submits the form and generates the redemption code, the worst case will take 4 minutes to respond to the user, but of course it can be generated asynchronously with Message Queuing, but why uniqid need so much time to generate a simple string?
Then go to see the implementation of Uniqid source code, paste the following
Php_function (uniqid) {char *prefix = ""; #if defined (__cygwin__) zend_bool more_entropy = 1; #else zend_bool mor e_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 ' more entropy ' 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 has are 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);}
There is no complicated operation for logic, and it is a simple process for the present time seconds and microseconds, and then a simple test is written.
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;}
It takes 2000 microseconds to execute 1W times, so why? However, we find that there is a lot of duplication in the generated UID, which is to note the Usleep function in the original code.
Coupled with the Usleep function in the test, this time and PHP results consistent also takes nearly 40 seconds, usleep here in order to maintain a different generation of UID.
The problem appears in the Usleep function, and then in the Usleep before and after the time interval, 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;
Finally found to generate 1W Prize code requires 39.99587739.995,877 seconds, while the usleep interval sum of 39.982442m, by printing usleep time to find each usleep (1) from the process hangs to wake up to 4000 microseconds, Originally knew Usleep not reach the precision, also also difference is too far point.
Finally, the following code is used to generate the prize code
/** * Generate redemption Code and save to database return Setno * $pageId activity ID * $level Prize level * s number of prizes generated */public static funct Ion Generatecdkeyandsave ($pageId, $level, s) {$level 1Prefix =array (2,5,9, ' E ', ' F ', ' M ', ' N ', ' Q ', ' K ', ' Z ');// The first prize of the prefix $level 2Prefix =array (1,3,7, ' A ', ' C ', ' J ', ' R ', ' U ', ' V ', ' X ');//second prize of the predecessor $level 3Prefix = Array (4,6,8, ' B ', ' D ', ' G ', ' H ', ' I ', ' L ', ' O ', ' P ', ' R ', ' S ', ' T ', ' W ', ' Y ');//The prefix of the third prize if (Empty ($pageId) | | empty ($level) | | empty (s)) return False $levelPrefix = $level 1Prefix; if ($level ==2) $levelPrefix = $level 2Prefix; if ($level ==3) $levelPrefix = $level 3Prefix; $codes =array (); $now = time (); for ($i =0; $i < s; $i + +) {$prefixKey = Array_rand ($levelPrefix); $prefix = Self::coupon_prefix. $levelPrefix [$prefixKey]; $code =base_convert (HEXDEC (MD5 (UNIQID ())), 10,26); The server above uniqid performs slow dying//$code =base_convert (HEXDEC (MD5 ($pageId. ' a#1$v& '. $i)), 10,26);//Data too much HEXDECLoss of a large amount of precision $code 1 = base_convert (substr (MD5 ($pageId. $i. $now), 0, 10), 16, 36); $code 2 = Base_convert ($i, 10, 26); $code 2Len = strlen ($code 2); if ($code 2Len = = 1) {$code 2. = Chr (for the rand (a). Chr () Chr (rand (82, 90)). } else if ($code 2Len = = 2) {$code 2. = Chr (rand (+ 82, 90)). chr (RAND); } else if ($code 2Len = = 3) {$code 2. = Chr (rand (82, 90)); } $code = $code 1. $code 2; $codes [] = $prefix. Strtoupper ($code); } return $codes; }
Companion UUID Test Code
#include
#include
#include
#include
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);}