Micro-optimization for UUID.fromString in 7 steps

public static UUID fromString(String name) {
String[] components = name.split("-");
if (components.length != 5)
throw new IllegalArgumentException("Invalid UUID string: "+name);
for (int i=0; i<5; i++)
components[i] = "0x"+components[i];

long mostSigBits = Long.decode(components[0]).longValue();
...
  1. It uses regular expressions;
  2. It creates an array and new strings (for our case it’s 1 allocation for an array and 5 allocations for components).

0. Replace String.split with a compiled pattern

private static final Pattern SPLIT_PATTERN = Pattern.compile("-");

public static UUID fromStringFast(String s)
{
String[] components = SPLIT_PATTERN.split(s);

1. Use indexOf/substring instead of split

int component1EndIndex = s.indexOf('-');
int component2EndIndex = s.indexOf('-', component1EndIndex + 1);
int component3EndIndex = s.indexOf('-', component2EndIndex + 1);
int component4EndIndex = s.indexOf('-', component3EndIndex + 1);

long mostSigBits = decode(s, 0, component1EndIndex);
mostSigBits <<= 16;
mostSigBits |=
decode(s, component1EndIndex + 1, component2EndIndex);
...private static long decode(String s, int from, int to) {
return Long.decode("0x" + s.substring(from, to));
}

2. Don’t use concatenation

long mostSigBits = substringAndParseLong(s, 0, component1EndIndex);
mostSigBits <<= 16;
mostSigBits |= substringAndParseLong(s, component1EndIndex + 1, component2EndIndex);
...static long substringAndParseLong(String s, int from, int to) {
return parseLong(s.substring(from, to), 16);
}

3. Don’t use substring

long parseLong(String s, final int from, final int to)

4. Specific replacement for the Character.digit

digit = Character.digit(s.charAt(i++), radix);

5. Remove redundant checks for parseLong

Final. Combine Steps 4 and 5

Summary

  • Step 0 (unsuccessful): replace String.split(“-”) with Pattern.compile(“-”).split (String.split is optimized for it);
  • Step 1: replace String.split with indexOf/substring (-2 allocations for the ArrayList and the Array of Strings);
  • Step 2: remove concatenation (additional allocations) and using parseLong instead of heavy decode;
  • Step 3: don’t use substring at all (no more allocations) — create own parseLong implementation;
  • Step 4: replace Character.digit with the specific only hex implementation;
  • Step 5: remove all generic code from our parseLong implementation;
  • Final: combine Step 4 and Step 5.
Version    | Avg time, ns | Gain to previous | Gain to original
Original | 353 | 0 | 0
Step 0 | 473 | -120 (-34%) | -120 (-34%)
Step 1 | 340 | | 13 (4%)
Step 2 | 171 | 169 (50%) | 182 (~2 times)
Step 3 | 145 | 26 (15%) | 208 (~2.5 times)
Step 4 | 111 | 34 (23%) | 242 (~3 times)
Step 5 | 112 | -1 (-1%) | 241 (~3 times)
Final | 81 | 0 | 272 (~4.5 times)

Conclusion

--

--

--

Software developer, moved to Israel from Russia, trying to be aware of things.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Dmitry Komanov

Dmitry Komanov

Software developer, moved to Israel from Russia, trying to be aware of things.

More from Medium

Avoid switch case for ENUMs — Visitor pattern

InjectIntoKeyValue in Eclipse Collections

The Semantic Emptiness of Objects in the If Statement

Task.Delay vs Thread.Sleep