Reputation: 25854
Given the following code snippet, which generates a UUID.randomUUID()
, I get the following performance results (in milliseconds):
public static void main(String[] args) {
long tmp = System.currentTimeMillis();
UUID.randomUUID();
tmp = printDiff(tmp);
UUID.randomUUID();
tmp = printDiff(tmp);
UUID.randomUUID();
tmp = printDiff(tmp);
UUID.randomUUID();
tmp = printDiff(tmp);
}
private static long printDiff(final long previousTimestamp) {
long tmp = System.currentTimeMillis();
System.out.printf("%s%n", tmp - previousTimestamp);
return tmp;
}
Results:
971
6
0
0
JDK: 1.8 OS: Windows 7
Why does only the initial call take so long? (Nearly 1 second!)
Upvotes: 5
Views: 3037
Reputation: 25
Based on the Java 8 codes, it looks like creating a SecureRandom
object is expensive.That's why they defer initialization until needed (aka lazy initialization) and reuse it in subsequent invocation.
/*
* The random number generator used by this class to create random
* based UUIDs. In a holder class to defer initialization until needed.
*/
private static class Holder {
static final SecureRandom numberGenerator = new SecureRandom();
}
Upvotes: 0
Reputation: 120848
it's the initialization of the SecureRandom that is done once:
//from the source code of randomUUID
private static class Holder {
static final SecureRandom numberGenerator = new SecureRandom();
}
But that's not all here. Those zeroes should really jump into your face. So the operation took 0 milliseconds; does it mean they took less? like a few nano-seconds or you are doing something wrong?
There's a proper tool to measure this things, called jmh.
@BenchmarkMode({ Mode.AverageTime, Mode.SingleShotTime })
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class UUIDRandom {
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder().include(UUIDRandom.class.getSimpleName()).build();
new Runner(opt).run();
}
@Benchmark
@Fork(1)
public UUID random() {
return UUID.randomUUID();
}
}
and the output says:
Benchmark Mode Cnt Score Error Units
UUIDRandom.random avgt 2 0.002 ms/op
UUIDRandom.random ss 2 0.094 ms/op
Indeed single-shot time is far worse then the average.
Upvotes: 7
Reputation: 33466
The first time UUID.randomUUID()
is called, it has to initialize some internal objects which it uses in all subsequent calls as well.
The source code for UUID.randomUUID
is:
public static UUID randomUUID() {
SecureRandom ng = Holder.numberGenerator;
byte[] randomBytes = new byte[16];
ng.nextBytes(randomBytes);
randomBytes[6] &= 0x0f; /* clear version */
randomBytes[6] |= 0x40; /* set to version 4 */
randomBytes[8] &= 0x3f; /* clear variant */
randomBytes[8] |= 0x80; /* set to IETF variant */
return new UUID(randomBytes);
}
Here, Holder.numberGenerator
is a global variable which, on first use, has to be initialized:
private static class Holder {
static final SecureRandom numberGenerator = new SecureRandom();
}
Upvotes: 0