为什么初始调用 UUID.randomUUID() 很慢?
Why is the initial call to UUID.randomUUID() slow?
给定以下生成 UUID.randomUUID()
的代码片段,我得到以下性能结果(以毫秒为单位):
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;
}
结果:
971
6
0
0
JDK: 1.8
OS: Windows 7
为什么只有初次通话需要这么长时间? (将近 1 秒!)
第一次调用 UUID.randomUUID()
时,它必须初始化它在所有后续调用中使用的一些内部对象。
source code for UUID.randomUUID
是:
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);
}
这里的Holder.numberGenerator
是一个全局变量,第一次使用需要初始化:
private static class Holder {
static final SecureRandom numberGenerator = new SecureRandom();
}
一次完成的是SecureRandom的初始化:
//from the source code of randomUUID
private static class Holder {
static final SecureRandom numberGenerator = new SecureRandom();
}
但这还不是全部。那些零真的应该跳到你的脸上。所以操作耗时0毫秒;这是否意味着他们拿的少了?喜欢一些 nano-seconds 或者你做错了什么?
有一个合适的工具可以衡量这些东西,叫做 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();
}
}
并且输出显示:
Benchmark Mode Cnt Score Error Units
UUIDRandom.random avgt 2 0.002 ms/op
UUIDRandom.random ss 2 0.094 ms/op
确实 single-shot 时间比平均水平差得多。
基于 Java 8 个代码,看起来创建一个 SecureRandom
对象是 expensive.That 为什么他们推迟初始化直到需要(也称为惰性初始化)并在后续调用。
/*
* 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();
}
给定以下生成 UUID.randomUUID()
的代码片段,我得到以下性能结果(以毫秒为单位):
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;
}
结果:
971
6
0
0
JDK: 1.8 OS: Windows 7
为什么只有初次通话需要这么长时间? (将近 1 秒!)
第一次调用 UUID.randomUUID()
时,它必须初始化它在所有后续调用中使用的一些内部对象。
source code for UUID.randomUUID
是:
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);
}
这里的Holder.numberGenerator
是一个全局变量,第一次使用需要初始化:
private static class Holder {
static final SecureRandom numberGenerator = new SecureRandom();
}
一次完成的是SecureRandom的初始化:
//from the source code of randomUUID
private static class Holder {
static final SecureRandom numberGenerator = new SecureRandom();
}
但这还不是全部。那些零真的应该跳到你的脸上。所以操作耗时0毫秒;这是否意味着他们拿的少了?喜欢一些 nano-seconds 或者你做错了什么?
有一个合适的工具可以衡量这些东西,叫做 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();
}
}
并且输出显示:
Benchmark Mode Cnt Score Error Units
UUIDRandom.random avgt 2 0.002 ms/op
UUIDRandom.random ss 2 0.094 ms/op
确实 single-shot 时间比平均水平差得多。
基于 Java 8 个代码,看起来创建一个 SecureRandom
对象是 expensive.That 为什么他们推迟初始化直到需要(也称为惰性初始化)并在后续调用。
/*
* 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();
}