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

Reply via email to