diff --git a/README.md b/README.md index ab5b204..d684e53 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,8 @@ This implementation does away with the string concatenation (and resulting strin | `UUID#fromString(String)` | 2,613,730 UUIDs/second | ± 25,278 UUIDs/second | | `FastUUID#parseUUID(String)` | 16,796,302 UUIDs/second | ± 191,695 UUIDs/second | +Java 15 uses a [much improved method](https://github.com/openjdk/jdk/commit/ebadfaeb2e1cc7b5ce5f101cd8a539bc5478cf5b) to parse UUIDs from strings. As a result, we just pass calls to `FastUUID#parseUUID(String)` through to `UUID#fromString(String)` under Java 15 and newer. + ### UUIDs to strings We've shown that we can significantly improve upon the stock `UUID#fromString(String)` implementation. Can we achieve similar gains in going from a `UUID` to a `String`? Let's take a look at the stock implementation of `UUID#toString()` from Java 8: diff --git a/pom.xml b/pom.xml index e27a634..245e0b1 100644 --- a/pom.xml +++ b/pom.xml @@ -48,21 +48,21 @@ junit junit - 4.13.1 + 4.13.2 test org.openjdk.jmh jmh-core - 1.21 + 1.32 test org.openjdk.jmh jmh-generator-annprocess - 1.21 + 1.32 test @@ -110,7 +110,7 @@ org.openjdk.jmh jmh-generator-annprocess - 1.21 + 1.32 diff --git a/src/benchmark/java/com/eatthepath/UUIDBenchmark.java b/src/benchmark/java/com/eatthepath/UUIDBenchmark.java index 025097e..21e88ae 100644 --- a/src/benchmark/java/com/eatthepath/UUIDBenchmark.java +++ b/src/benchmark/java/com/eatthepath/UUIDBenchmark.java @@ -40,7 +40,8 @@ public class UUIDBenchmark { private final UUID[] uuids = new UUID[PREGENERATED_UUID_COUNT]; private final String[] uuidStrings = new String[PREGENERATED_UUID_COUNT]; - private int i = 0; + // Using a long here because on very fast machines the int will overflow + private long i = 0; @Setup public void setup() { @@ -54,21 +55,27 @@ public void setup() { @Benchmark public UUID benchmarkUUIDFromString() { - return UUID.fromString(this.uuidStrings[this.i++ % PREGENERATED_UUID_COUNT]); + return UUID.fromString(this.uuidStrings[(int) (this.i++ % PREGENERATED_UUID_COUNT)]); } @Benchmark public UUID benchmarkUUIDFromFastParser() { - return FastUUID.parseUUID(this.uuidStrings[this.i++ % PREGENERATED_UUID_COUNT]); + return FastUUID.parseUUID(this.uuidStrings[(int) (this.i++ % PREGENERATED_UUID_COUNT)]); + } + + // Checking if type-check to String won't affect performance + @Benchmark + public UUID benchmarkUUIDFromCharSequenceFastParser() { + return FastUUID.parseUUID((CharSequence) this.uuidStrings[(int) (this.i++ % PREGENERATED_UUID_COUNT)]); } @Benchmark public String benchmarkUUIDToString() { - return this.uuids[this.i++ % PREGENERATED_UUID_COUNT].toString(); + return this.uuids[(int) (this.i++ % PREGENERATED_UUID_COUNT)].toString(); } @Benchmark public String benchmarkFastParserToString() { - return FastUUID.toString(this.uuids[this.i++ % PREGENERATED_UUID_COUNT]); + return FastUUID.toString(this.uuids[(int) (this.i++ % PREGENERATED_UUID_COUNT)]); } } diff --git a/src/main/java/com/eatthepath/uuid/FastUUID.java b/src/main/java/com/eatthepath/uuid/FastUUID.java index 451839e..6c356cf 100644 --- a/src/main/java/com/eatthepath/uuid/FastUUID.java +++ b/src/main/java/com/eatthepath/uuid/FastUUID.java @@ -41,6 +41,7 @@ public class FastUUID { private static final boolean USE_JDK_UUID_TO_STRING; + private static final boolean USE_JDK_UUID_FROM_STRING; static { int majorVersion = 0; @@ -53,6 +54,7 @@ public class FastUUID { } USE_JDK_UUID_TO_STRING = majorVersion >= 9; + USE_JDK_UUID_FROM_STRING = majorVersion >= 15; } private static final int UUID_STRING_LENGTH = 36; @@ -107,6 +109,35 @@ private FastUUID() { * described in {@link UUID#toString()} */ public static UUID parseUUID(final CharSequence uuidSequence) { + if (USE_JDK_UUID_FROM_STRING && uuidSequence instanceof String) { + // OpenJDK 15 and newer use a faster method for parsing UUIDs + return UUID.fromString((String) uuidSequence); + } + + return parseUUIDInternal(uuidSequence); + } + + /** + * Parses a UUID from the given string. The string must represent a UUID as described in + * {@link UUID#toString()}. + * + * @param uuidString the string from which to parse a UUID + * + * @return the UUID represented by the given string + * + * @throws IllegalArgumentException if the given string does not conform to the string representation as + * described in {@link UUID#toString()} + */ + public static UUID parseUUID(final String uuidString) { + if (USE_JDK_UUID_FROM_STRING) { + // OpenJDK 15 and newer use a faster method for parsing UUIDs + return UUID.fromString(uuidString); + } + + return parseUUIDInternal(uuidString); + } + + private static UUID parseUUIDInternal(final CharSequence uuidSequence) { if (uuidSequence.length() != UUID_STRING_LENGTH || uuidSequence.charAt(8) != '-' || uuidSequence.charAt(13) != '-' ||