Hi Brett,

You might be trying to over optimize, making the source more complex than is useful or necessary. I think it is likely that on most architectures the varhandles take advantage of appropriate machine instructions and both have the same performance. I expect the performance difference to be barely noticeable, and if so keep it simple and direct.

Even so, a localized static cache inUUID of whichever VarHandle is native would serve the purpose better than a runtime check.

Regards, Roger


On 3/5/23 6:49 PM, Brett Okken wrote:
The new ByteArray class works great for the nameUUIDFromBytes method,
which must be in big endian.
For randomUUID, byte order does not matter, so using native would be
fastest, but there does not appear to be a utility class for that.
Is there a preference of just having a native order VarHandle to use
in UUID vs. having a utility method which chooses which utility class
to call based on the native order vs. some other option?

Thanks,
Brett

On Wed, Mar 1, 2023 at 9:08 AM Roger Riggs <roger.ri...@oracle.com> wrote:
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://urldefense.com/v3/__https://github.com/openjdk/jdk/pull/12076__;!!ACWV5N9M2RV99hQ!LF6TU_iFxi-lfSpZXjna9bCMPRs_gRvdXsfL5Tih6k2oYN94Hfb3sV_gBzdTvty-gXm0rzSgEZ0_xdQdYa7b05LArw$

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

Reply via email to