I'm trying to create globally-unique identifiers in JavaScript. I'm not sure what routines are available on all browsers, how "random" and seeded the built-in random number generator is, etc..
The GUID / UUID should be at least 32 characters and should stay in the ASCII range to avoid trouble when passing them around.
broofa's answer is pretty slick, indeed - impressively clever, really... rfc4122 compliant, somewhat readable, and compact. Awesome!
But if you're looking at that regular expression, those many
replace()
callbacks,toString()
's andMath.random()
function calls (where he's only using 4 bits of the result and wasting the rest), you may start to wonder about performance. Indeed, joelpt even decided to toss out RFC for generic GUID speed withgenerateQuickGUID
.But, can we get speed and RFC compliance? I say, YES! Can we maintain readability? Well... Not really, but it's easy if you follow along.
But first, my results, compared to broofa,
guid
(the accepted answer), and the non-rfc-compliantgenerateQuickGuid
:So by my 6th iteration of optimizations, I beat the most popular answer by over 12X, the accepted answer by over 9X, and the fast-non-compliant answer by 2-3X. And I'm still rfc4122 compliant.
Interested in how? I've put the full source on http://jsfiddle.net/jcward/7hyaC/3/ and on http://jsperf.com/uuid-generator-opt/4
For an explanation, let's start with broofa's code:
So it replaces
x
with any random hex digit,y
with random data (except forcing the top 2 bits to10
per the RFC spec), and the regex doesn't match the-
or4
characters, so he doesn't have to deal with them. Very, very slick.The first thing to know is that function calls are expensive, as are regular expressions (though he only uses 1, it has 32 callbacks, one for each match, and in each of the 32 callbacks it calls Math.random() and v.toString(16)).
The first step toward performance is to eliminate the RegEx and its callback functions and use a simple loop instead. This means we have to deal with the
-
and4
characters whereas broofa did not. Also, note that we can use String Array indexing to keep his slick String template architecture:Basically, the same inner logic, except we check for
-
or4
, and using a while loop (instead ofreplace()
callbacks) gets us an almost 3X improvement!The next step is a small one on the desktop but makes a decent difference on mobile. Let's make fewer Math.random() calls and utilize all those random bits instead of throwing 87% of them away with a random buffer that gets shifted out each iteration. Let's also move that template definition out of the loop, just in case it helps:
This saves us 10-30% depending on platform. Not bad. But the next big step gets rid of the toString function calls altogether with an optimization classic - the look-up table. A simple 16-element lookup table will perform the job of toString(16) in much less time:
The next optimization is another classic. Since we're only handling 4-bits of output in each loop iteration, let's cut the number of loops in half and process 8-bits each iteration. This is tricky since we still have to handle the RFC compliant bit positions, but it's not too hard. We then have to make a larger lookup table (16x16, or 256) to store 0x00 - 0xff, and we build it only once, outside the e5() function.
I tried an e6() that processes 16-bits at a time, still using the 256-element LUT, and it showed the diminishing returns of optimization. Though it had fewer iterations, the inner logic was complicated by the increased processing, and it performed the same on desktop, and only ~10% faster on mobile.
The final optimization technique to apply - unroll the loop. Since we're looping a fixed number of times, we can technically write this all out by hand. I tried this once with a single random variable r that I kept re-assigning, and performance tanked. But with four variables assigned random data up front, then using the lookup table, and applying the proper RFC bits, this version smokes them all:
Modualized: http://jcward.com/UUID.js -
UUID.generate()
The funny thing is, generating 16 bytes of random data is the easy part. The whole trick is expressing it in String format with RFC compliance, and it's most tightly accomplished with 16 bytes of random data, an unrolled loop and lookup table.
I hope my logic is correct -- it's very easy to make a mistake in this kind of tedious bit-work. But the outputs look good to me. I hope you enjoyed this mad ride through code optimization!
Be advised: my primary goal was to show and teach potential optimization strategies. Other answers cover important topics such as collisions and truly random numbers, which are important for generating good UUIDs.
Simple JavaScript module as a combination of best answers in this thread.
Usage:
The better way:
Minimized:
For those wanting an rfc4122 version 4 compliant solution with speed considerations (few calls to Math.random()):
The above function should have a decent balance between speed and randomness.
You can use node-uuid (https://github.com/kelektiv/node-uuid)
Simple, fast generation of RFC4122 UUIDS.
Features:
Install Using NPM:
Or Using uuid via browser:
Download Raw File (uuid v1): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v1.js Download Raw File (uuid v4): https://raw.githubusercontent.com/kelektiv/node-uuid/master/v4.js
Want even smaller? Check this out: https://gist.github.com/jed/982883
Usage:
ES6:
Weird that no one has mentioned this yet but for completeness, there's a plethora of guid generators on npm I'm willing to bet most of them work in browser too.