Generate UUID


#1

Anyone have a fast way of generating a verson 4 UUID?

I have found some libraries to do this, but it seems like it could be
done in about 8 lines of code. Any faster approches?

what about this?

def rand_hex(l)
validChars = (“A”…“F”).to_a + (“0”…“9”).to_a
length = validChars.size
hexCode = “”
1.upto(l) do |i|
hexCode << validChars[rand(length-1)]
end
hexCode
end

r =
“#{rand_hex(8)}-#{rand_hex(4)}-#{rand_hex(4)}-#{rand_hex(4)}-#{rand_hex(12)}”
puts r

thanks


#2

On 08.03.2007 22:44, Aaron S. wrote:

hexCode = ""
1.upto(l) do |i|
  hexCode << validChars[rand(length-1)]
end
hexCode

end

r =
“#{rand_hex(8)}-#{rand_hex(4)}-#{rand_hex(4)}-#{rand_hex(4)}-#{rand_hex(12)}”
puts r

Make validChars and length constants declared outside the method. And
also, use rand(length) otherwise you never get a “9”. You could also
use ?A…?F and ?0…?9 - no idea whether that makes a performance diff.

But I guess this one is even faster:

def rand_hex_2(l)
rand(1 << (l*4)).to_a(16)
end

Kind regards

robert


#3

Robert K. wrote:

On 08.03.2007 22:44, Aaron S. wrote:

hexCode = ""
1.upto(l) do |i|
  hexCode << validChars[rand(length-1)]
end
hexCode

end

r =
“#{rand_hex(8)}-#{rand_hex(4)}-#{rand_hex(4)}-#{rand_hex(4)}-#{rand_hex(12)}”
puts r

Make validChars and length constants declared outside the method. And
also, use rand(length) otherwise you never get a “9”. You could also
use ?A…?F and ?0…?9 - no idea whether that makes a performance diff.

But I guess this one is even faster:

def rand_hex_2(l)
rand(1 << (l*4)).to_a(16)
end

Kind regards

robert

def rand_hex_2(l)
rand (1 << (l*4) ).to_a(16)
end
puts rand_hex_2(9)

ArgumentError: wrong number of arguments (1 for 0)


#4

He probably meant to_s(16), not to_a(16). Anyway, rand_hex_2 won’t
zero-pad, so you might want to replace the to_s with a format
operator:

def rand_hex_3(l)
“%0#{n}x” % rand(1 << n*4)
end

def rand_uuid
[8,4,4,4,12].map {|n| rand_hex_3(n)}.join(’-’)
end

puts rand_uuid


#5

Eden Li wrote:

He probably meant to_s(16), not to_a(16). Anyway, rand_hex_2 won’t
zero-pad, so you might want to replace the to_s with a format
operator:

def rand_hex_3(l)
“%0#{n}x” % rand(1 << n*4)
end

def rand_uuid
[8,4,4,4,12].map {|n| rand_hex_3(n)}.join(’-’)
end

puts rand_uuid

cool, thanks. just to clarify. couple variable names needed changing

def rand_hex_3(l)
“%0#{l}x” % rand(1 << l*4)
end

def rand_uuid
[8,4,4,4,12].map {|n| rand_hex_3(n)}.join(’-’)
end

puts rand_uuid


#6

On Mar 8, 2007, at 5:38 PM, Aaron S. wrote:

We’re using uuidtools at work and it is quite possibly the worst code
I’ve ever come across. Convoluted, obfuscated for the sake of
“security” and slow as hell.

% ./blah.rb 100

uuidtools-original 0.130000 4.650000 4.780000 ( 4.782855)

Yes, 21 uuids per second!

I got a 400x improvement out of it by dropping this into our config/
environment.rb:

def UUID.true_random
(1…8).to_a.map { rand(0x10000) }.pack(“n8”)
end

% ./blah.rb 100000
./blah.rb:9: warning: redefine true_random

uuidtools-modified 11.470000 0.010000 11.480000 ( 11.490444)

def rand_hex_3(l)
“%0#{l}x” % rand(1 << l*4)
end

def rand_uuid
[8,4,4,4,12].map {|n| rand_hex_3(n)}.join(’-’)
end

This is clever. I really like it. To take the above solution a couple
steps further:

% ./blah.rb 1000000

of iterations = 1000000

                       user     system      total        real

null_time 0.120000 0.000000 0.120000 ( 0.116494)
benchmark-1 28.370000 0.050000 28.420000 ( 28.628063) #
original
benchmark-2 15.320000 0.010000 15.330000 ( 15.339800) #
inline and remove join
benchmark-3 10.500000 0.000000 10.500000 ( 10.521271) #
unroll the loop, fully
benchmark-4 8.600000 0.010000 8.610000 ( 8.614185) #
reorg to remove bignum


require ‘benchmark’

max = (ARGV.shift || 1_000_000).to_i

def rand_hex_3(l)
“%0#{l}x” % rand(1 << l*4)
end

def rand_uuid # original
[8,4,4,4,12].map {|n| rand_hex_3(n)}.join(’-’)
end

def rand_uuid2 # inline and remove join
“%08x-%04x-%04x-%04x-%012x” % [8,4,4,4,12].map {|n| rand(1 << n*4) }
end

def rand_uuid3 # unroll the loop
“%08x-%04x-%04x-%04x-%012x” % [
rand(0x0000100000000),
rand(0x0000000010000),
rand(0x0000000010000),
rand(0x0000000010000),
rand(0x1000000000000),
]
end

def rand_uuid4 # remove bignums
“%04x%04x-%04x-%04x-%04x-%06x%06x” % [
rand(0x0010000),
rand(0x0010000),
rand(0x0010000),
rand(0x0010000),
rand(0x0010000),
rand(0x1000000),
rand(0x1000000),
]
end

puts “# of iterations = #{max}”
Benchmark::bm(20) do |x|
x.report(“null_time”) do
for i in 0…max do
# do nothing
end
end

x.report(“benchmark-1”) do
for i in 0…max do
rand_uuid
end
end

x.report(“benchmark-2”) do
for i in 0…max do
rand_uuid2
end
end

x.report(“benchmark-3”) do
for i in 0…max do
rand_uuid3
end
end

x.report(“benchmark-4”) do
for i in 0…max do
rand_uuid4
end
end
end


#7

On Mar 11, 2007, at 5:16 PM, Gary W. wrote:

I couldn’t tell from the message if Ryan or Aaron wrote the
parts I quoted above…

I wrote all those parts.

I’m not an expert on UUIDs but I think there is a real
semantic difference between a UUID and a random number
of the same (binary) length. RFC 4122 has lots of good
information on UUIDs: http://www.ietf.org/rfc/rfc4122.txt

No, you’re right, but for the most part, it really doesn’t matter for
any of my company’s uses. The most popular UUID format I’ve seen uses
the mac address as part of the encoding. I don’t know of any
portable way for a ruby script to get that, and with all our UUIDs
being generated from a single server for the foreseeable future…
yeah. Not too useful.

In the specific case of uuidtools, it actually goes through WAY more
convolutions that ever necessary and (according to the linux manpage
on /dev/urandom) actually REDUCES the randomness of the UUIDs that it
generates. I just couldn’t live with my test times anymore. 30% was
being spent on generating UUIDs that weren’t any better than my
random blob. Remember, 21 uuids per second. blech

I mostly wrote the rest of the code to illustrate additional methods
to get more performance out of (pure) ruby.


#8

On Mar 11, 2007, at 6:00 PM, Ryan D. wrote:

Yes, 21 uuids per second!

I got a 400x improvement out of it by dropping this into our config/
environment.rb:

def UUID.true_random
(1…8).to_a.map { rand(0x10000) }.pack(“n8”)
end

I couldn’t tell from the message if Ryan or Aaron wrote the
parts I quoted above…

I’m not an expert on UUIDs but I think there is a real
semantic difference between a UUID and a random number
of the same (binary) length. RFC 4122 has lots of good
information on UUIDs: http://www.ietf.org/rfc/rfc4122.txt

Gary W.


#9

For what i’m working with it won’t really matter. Trying to use as
little 3rd party libraries as possible. Not that yours isn’t good
enough, just not wanting to add more dependencies to the project.


#10

You might want to consider renaming them in your project so that
someone else that inherits your code won’t think that they’re really
universally unique. As it stands right now, the methods above will
definitely allow collisions, and will cause headaches (and possible
data loss…) if someone uses them with that assumption in mind.


#11

On Mar 11, 6:04 pm, Ryan D. removed_email_address@domain.invalid wrote:

yeah. Not too useful.
A random number will work fine if you want something that looks like a
UUID. But if you want a Universally Unique Identifier, the time-based
UUID works better. It uses the clock time and a restart sequence to
gaurantee unique identifiers for the same machine, combined with the
Mac address to guarantee universal uniqueness (aka unique in space and
time).

On Windows you can use ipconfig to get the MAC address, and on Linux/
BSD/OS/X use ifconfig. There’s no portable way, but you can run both
and see which answer you get.

Which is exactly what this UUID library does (disclosure: I’m the
author):

http://rubyforge.org/projects/reliable-msg

These are the numbers I get for generating 1 million UUIDs on a Duo
Core 2. Hopefully they’re fast enough for you:

cat test.rb
require “lib/uuid.rb”
1000000.times { UUID.new }

time ruby test.rb
uuid: Initialized UUID generator with sequence number 0x6832 and MAC
address 00:13:A9:8A:FA:AA

real 0m27.983s
user 0m18.796s
sys 0m9.051s

Assaf
http://labnotes.org