Is there any interest in updating the static UUID.randomUUID() and UUID.nameUUIDFromBytes(byte[]) factory methods to use either a ByteBuffer or byteArrayViewVarHandle to convert the byte[] to 2 long values then do the bit twiddling? These methods are really dominated by time to create/populate the byte[], but this does reduce the time to create the 2 long values by at least half. It would also allow the removal of the private UUID(byte[] data).
public static UUID randomUUID() { SecureRandom ng = Holder.numberGenerator; byte[] randomBytes = new byte[16]; ng.nextBytes(randomBytes); final ByteBuffer bb = ByteBuffer.wrap(randomBytes); bb.order(ByteOrder.nativeOrder()); long msb = bb.getLong(); long lsb = bb.getLong(); msb &= 0xFFFFFFFFFFFF0FFFL; /* clear version */ msb |= 0x4000L; /* set to version 4 */ lsb &= 0x3FFFFFFFFFFFFFFFL; /* clear variant */ lsb |= 0x8000000000000000L; /* set to IETF variant */ return new UUID(msb, lsb); } public static UUID nameUUIDFromBytes(byte[] name) { MessageDigest md; try { md = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException nsae) { throw new InternalError("MD5 not supported", nsae); } byte[] md5Bytes = md.digest(name); // default byte order is BIG_ENDIAN final ByteBuffer bb = ByteBuffer.wrap(md5Bytes); long msb = bb.getLong(); long lsb = bb.getLong(); msb &= 0xFFFFFFFFFFFF0FFFL; /* clear version */ msb |= 0x3000L; /* set to version 3 */ lsb &= 0x3FFFFFFFFFFFFFFFL; /* clear variant */ lsb |= 0x8000000000000000L; /* set to IETF variant */ return new UUID(msb, lsb); } Benchmark Mode Cnt Score Error Units UUIDBenchmark.jdk_name avgt 3 11.885 ± 4.025 ns/op UUIDBenchmark.jdk_random avgt 3 11.656 ± 0.987 ns/op UUIDBenchmark.longs avgt 3 7.618 ± 1.047 ns/op UUIDBenchmark.longs_bb avgt 3 7.755 ± 1.643 ns/op UUIDBenchmark.longs_name avgt 3 8.467 ± 1.784 ns/op UUIDBenchmark.longs_name_bb avgt 3 8.455 ± 1.662 ns/op UUIDBenchmark.randomBytes avgt 3 6.132 ± 0.447 ns/op @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS) @Fork(1) @State(Scope.Benchmark) public class UUIDBenchmark { private static final VarHandle LONGS_ACCESS = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.nativeOrder()); private static final VarHandle BE_LONGS_ACCESS = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); @Benchmark public byte[] randomBytes() { final byte[] bytes = new byte[16]; randomBytes(bytes); return bytes; } @Benchmark public void jdk_random(Blackhole bh) { final byte[] data = new byte[16]; randomBytes(data); data[6] &= 0x0f; /* clear version */ data[6] |= 0x40; /* set to version 4 */ data[8] &= 0x3f; /* clear variant */ data[8] |= 0x80; /* set to IETF variant */ long msb = 0; long lsb = 0; assert data.length == 16 : "data must be 16 bytes in length"; for (int i=0; i<8; i++) msb = (msb << 8) | (data[i] & 0xff); for (int i=8; i<16; i++) lsb = (lsb << 8) | (data[i] & 0xff); bh.consume(msb); bh.consume(lsb); } @Benchmark public void jdk_name(Blackhole bh) { final byte[] md5Bytes = new byte[16]; randomBytes(md5Bytes); md5Bytes[6] &= 0x0f; /* clear version */ md5Bytes[6] |= 0x30; /* set to version 3 */ md5Bytes[8] &= 0x3f; /* clear variant */ md5Bytes[8] |= 0x80; /* set to IETF variant */ long msb = 0; long lsb = 0; assert md5Bytes.length == 16 : "data must be 16 bytes in length"; for (int i=0; i<8; i++) msb = (msb << 8) | (md5Bytes[i] & 0xff); for (int i=8; i<16; i++) lsb = (lsb << 8) | (md5Bytes[i] & 0xff); bh.consume(msb); bh.consume(lsb); } @Benchmark public void longs(Blackhole bh) { final byte[] data = new byte[16]; randomBytes(data); long msb = (long) LONGS_ACCESS.get(data, 0); long lsb = (long) LONGS_ACCESS.get(data, 8); msb &= 0xFFFFFFFFFFFF0FFFL; msb |= 0x4000L; lsb &= 0x3FFFFFFFFFFFFFFFL; lsb |= 0x8000000000000000L; bh.consume(msb); bh.consume(lsb); } @Benchmark public void longs_name(Blackhole bh) { final byte[] data = new byte[16]; randomBytes(data); long msb = (long) BE_LONGS_ACCESS.get(data, 0); long lsb = (long) BE_LONGS_ACCESS.get(data, 8); msb &= 0xFFFFFFFFFFFF0FFFL; msb |= 0x3000L; lsb &= 0x3FFFFFFFFFFFFFFFL; lsb |= 0x8000000000000000L; bh.consume(msb); bh.consume(lsb); } @Benchmark public void longs_bb(Blackhole bh) { final byte[] data = new byte[16]; randomBytes(data); final ByteBuffer bb = ByteBuffer.wrap(data); bb.order(ByteOrder.nativeOrder()); long msb = bb.getLong(); long lsb = bb.getLong(); msb &= 0xFFFFFFFFFFFF0FFFL; msb |= 0x4000L; lsb &= 0x3FFFFFFFFFFFFFFFL; lsb |= 0x8000000000000000L; bh.consume(msb); bh.consume(lsb); } @Benchmark public void longs_name_bb(Blackhole bh) { final byte[] data = new byte[16]; randomBytes(data); final ByteBuffer bb = ByteBuffer.wrap(data); // bb.order(ByteOrder.BIG_ENDIAN); long msb = bb.getLong(); long lsb = bb.getLong(); msb &= 0xFFFFFFFFFFFF0FFFL; msb |= 0x3000L; lsb &= 0x3FFFFFFFFFFFFFFFL; lsb |= 0x8000000000000000L; bh.consume(msb); bh.consume(lsb); } static void randomBytes(byte[] bytes) { ThreadLocalRandom tlr = ThreadLocalRandom.current(); LONGS_ACCESS.set(bytes, 0, tlr.nextLong()); LONGS_ACCESS.set(bytes, 8, tlr.nextLong()); } }