5

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!)

riddle_me_this
  • 8,575
  • 10
  • 55
  • 80
Nick Grealy
  • 24,216
  • 9
  • 104
  • 119
  • @4castle its not, there are only 4 invocations here, there's no JIT – Eugene Jan 23 '17 at 00:44
  • 1
    Initialization of SecureRandom can be really slow on Windows due to network interface scanning etc. See [this question](http://stackoverflow.com/questions/38942514/simple-java-program-100-times-slower-after-plugging-in-usb-hotspot) for details. – apangin Jan 23 '17 at 07:22

3 Answers3

7

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.

Eugene
  • 117,005
  • 15
  • 201
  • 306
0

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();
}
4castle
  • 32,613
  • 11
  • 69
  • 106
0

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();
}