UUID creation performance

Roger Riggs roger.riggs at oracle.com
Wed Mar 1 15:07:20 UTC 2023


Hi,

That's an interesting idea.  Recently VarHandle access methods were 
created by JDK-8300236 [1] [2]
in the jdk.internal.util package. See the ByteArray and 
ByteArrayLittleEndian classes.

See how that would affect performance and leverage existing VarHandles.

Thanks, Roger

[1] https://bugs.openjdk.org/browse/JDK-8300236
[2] https://github.com/openjdk/jdk/pull/12076

On 3/1/23 7:50 AM, Brett Okken wrote:
> 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());
>      }
> }



More information about the core-libs-dev mailing list