Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Performance After Eight (Oracle Code One 2018)

Performance After Eight (Oracle Code One 2018)

Slides from the DEV5523 Oracle Code One 2018 session.

Dmitry Vyazelenko

October 24, 2018
Tweet

More Decks by Dmitry Vyazelenko

Other Decks in Programming

Transcript

  1. About me • Senior Software Engineer @ Canoo Engineering AG,

    Switzerland • Disorganizer at JCrete (jcrete.org) and Jalba (jalba.scot) Unconferences • Contacts: vyazelenko.com, @DVyazelenko
  2. Agenda • Modular JDK: startup and footprint • JEP 243:

    Java-Level JVM Compiler Interface • JEP 248: Make G1 the Default Garbage Collector • JEP 254: Compact Strings • JEP 280: Indify String Concatenation • JEP 269: Convenience Factory Methods for Collections • JEP 259: Stack-Walking API • New APIs
  3. JDK 11 JDK 8 62 69 54 58 JVM startup

    time -jar -cp Time, ms perf stat -r50 ...
  4. JDK 11 JDK 8 76 78 62 69 54 58

    JVM startup time -p modlibs --module-path -jar -cp Time, ms
  5. JDK 11 JDK 8 56 76 78 62 69 54

    58 JVM startup time jlink -modlibs -modpath -jar -cp Time, ms
  6. JDK 11 JDK 8 42 56 73 78 72 69

    54 58 JVM startup time AOT jlink -modlibs -modpath -jar -cp Time, ms
  7. JDK 11 JDK 8 42 56 73 78 62 69

    54 58 49 39 JVM startup time AOT jlink -p modlibs --module-path -jar -cp CDS* Time, ms
  8. Summary Develop a Java based JVM compiler interface (JVMCI) enabling

    a compiler written in Java to be used by the JVM as a dynamic compiler. Goals • Allow a Java component programmed against the JVMCI to be loaded at runtime and used by the JVM’s compile broker. • Allow a Java component programmed against the JVMCI to be loaded at runtime and used by trusted Java code to install machine code in the JVM that can be called via a Java reference to the installed code.
  9. • Default GC changed in JDK 9 • See [3]

    Why you want to run Java 10 if you're using the G1 Garbage Collector • Here be dragons (see [2] What a difference a JVM makes? )
  10. import org.openjdk.jol.info.ClassLayout; import org.openjdk.jol.info.GraphLayout; import org.openjdk.jol.vm.VM; import static java.lang.System.out; public

    class StringLayout { public static void main(String[] args) { String value = "Lorem ipsum ... est laborum."; out.println(VM.current().details()); out.println(ClassLayout.parseClass(String.class).toPrintable()); out.println(GraphLayout.parseInstance(value).toFootprint()); } }
  11. JDK 8 java.lang.String object internals: OFFSET SIZE TYPE DESCRIPTION 0

    12 (object header) 12 4 char[] String.value 16 4 int String.hash 20 4 (loss due to the next object alignment) Instance size: 24 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total Total size in bytes: 936
  12. JDK 11 java.lang.String object internals: OFFSET SIZE TYPE DESCRIPTION 0

    12 (object header) 12 4 byte[] String.value 16 4 int String.hash 20 1 byte String.coder 21 3 (loss due to the next object alignment) Instance size: 24 bytes Space losses: 0 bytes internal + 3 bytes external = 3 bytes total Total size in bytes: 488
  13. Benchmark JDK 8 JDK 11 JDK 11 -XX:-CompressedStrings StringBenchmark.charAt_Latin1 65

    ns/op 67 ns/op 62 ns/op StringBenchmark.charAt_UTF16 64 ns/op 65 ns/op 63 ns/op StringBenchmark.toLowerCase_Latin1 54 ns/op 120 B/op 29 ns/op 56 B/op 47 ns/op 120 B/op StringBenchmark.toLowerCase_UTF16 114 ns/op 120 B/op 108 ns/op 176 B/op 119 ns/op 144 B/op StringBenchmark.equals_Latin1_Latin1 5 ns/op 8 ns/op 6 ns/op StringBenchmark.equals_Latin1_UTF16 5 ns/op 3 ns/op 6 ns/op
  14. public class StringMaxSize { public static void main(String[] args) {

    char[] data = new char[Integer.MAX_VALUE - 2]; data[0] = '\uD83D'; data[1] = '\uDC7B'; String value = new String(data); System.out.println(value.hashCode()); } } Exception in thread "main" java.lang.OutOfMemoryError: UTF16 String size is 2147483645, should be less than 1073741823 at java.base/java.lang.StringUTF16.newBytesFor(StringUTF16.java:46) at java.base/java.lang.StringUTF16.toBytes(StringUTF16.java:148) at java.base/java.lang.String.<init>(String.java:3023) at java.base/java.lang.String.<init>(String.java:249) at com.vyazelenko.perf.string.StringMaxSize.main(StringMaxSize.java:9)
  15. import static java.lang.System.currentTimeMillis; import static java.lang.System.out; public class StringConcatExplore {

    public static void main(String[] args) { log("msg", 42); } private static void log(String msg, int count) { out.println("[" + currentTimeMillis() + "]: " + msg + " (" + count + ")"); } }
  16. JDK 8 0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new

    #5 // class java/lang/StringBuilder 6: dup 7: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 10: ldc #7 // String [ 12: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: invokestatic #9 // Method java/lang/System.currentTimeMillis:()J 18: invokevirtual #10 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 21: ldc #11 // String ]: 23: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: aload_0 27: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 30: ldc #12 // String ( 32: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 35: iload_1 36: invokevirtual #13 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 39: ldc #14 // String ) 41: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 44: invokevirtual #15 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 47: invokevirtual #16 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 50: return
  17. JDK 11 0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 3: invokestatic

    #8 // Method java/lang/System.currentTimeMillis:()J 6: aload_0 7: iload_1 8: invokedynamic #9, 0 // InvokeDynamic #0:makeConcatWithConstants:(JLjava/lang/String;I)Ljava/lang/String; 13: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 16: return BootstrapMethods: 0: #44 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/ MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/ Object;)Ljava/lang/invoke/CallSite; Method arguments: #45 [\u0001]: \u0001 (\u0001)
  18. String concat strategies • Bytecode generator – BC_SB – empty

    StringBuilder – BC_SB_SIZED – pre-sized StringBuilder – BC_SB_SIZED_EXACT – exactly sized StringBuilder • MethodHandle combinators – MH_SB_SIZED – pre-sized StringBuilder – MH_SB_SIZED_EXACT – exactly sized StringBuilder – MH_INLINE_SIZED_EXACT (default) – byte[] + exact sizing
  19. StringConcatBenchmark JDK 8 142 ns/op 432 B/op JDK 11 50

    ns/op 80 B/op JDK 11 -Djava.lang.invoke.stringConcat=BC_SB 107 ns/op 280 B/op JDK 11 -Djava.lang.invoke.stringConcat=BC_SB_SIZED 80 ns/op 176 B/op JDK 11 -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT 67 ns/op 160 B/op JDK 11 -Djava.lang.invoke.stringConcat=MH_SB_SIZED 79 ns/op 176 B/op JDK 11 -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT 88 ns/op 216 B/op
  20. public class StringConcatLoopBenchmark { @Benchmark public String naive() { String

    result = ""; for (int i = 0; i < iterations; i++) { result += (i + " " + log() + "\n"); } return result; } @Benchmark public String proper() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < iterations; i++) { sb.append(i).append(" ").append(log()).append("\n"); } return sb.toString(); } }
  21. Benchmark JDK 8 JDK 11 StringConcatLoopBenchmark.naive 48 µs/op 384288 B/op

    8 µs/op 52880 B/op StringConcatLoopBenchmark.proper 9 µs/op 26496 B/op 4 µs/op 10328 B/op
  22. 32 16 40 40 56 80 112 232 240 432

    List.of() List.of() Collections.emptyList() List.of(1) Collections.singletonList(1) List.of(1,2) new ArrayList(1,2) new LinkedList(1,2) List.of(1..10) new ArrayList(1..10) new LinkedList(1..10) Footprint in bytes
  23. 32 16 40 40 56 104 136 232 264 456

    List.of() vs Collections.unmodifiableList() List.of() Collections.emptyList() List.of(1) Collections.singletonList(1) List.of(1,2) new ArrayList(1,2) new LinkedList(1,2) List.of(1..10) new ArrayList(1..10) new LinkedList(1..10) Footprint in bytes
  24. 40 16 40 32 56 272 296 208 280 656

    808 656 Set.of() vs Collections.unmodifiableSet() Set.of() Collections.emptySet() Set.of(1) Collections.singleton(1) Set.of(1,2) new HashSet(1,2) new LinkedHashSet(1,2) new TreeSet(1,2) Set.of(1..10) new HashSet(1..10) new LinkedHashSet(1..10) new TreeSet(1..10) Footprint in bytes
  25. public class ExceptionBenchmark { @Benchmark public Class<?> caller_class() throws Exception

    { return doCall(() -> { StackTraceElement[] stackTrace = new Exception().getStackTrace(); return Class.forName(stackTrace[2].getClassName()); }, stackDepth); } @Benchmark public int stack_depth() throws Exception { return doCall(() -> new Exception().getStackTrace().length, stackDepth); } @Benchmark public Object top_10_frames() throws Exception { return doCall(() -> { StackTraceElement[] stackTrace = new Exception().getStackTrace(); return Arrays.copyOfRange(stackTrace, 0, 10); }, stackDepth); } }
  26. public class StackWalkerBenchmark { public static final StackWalker STACK_WALKER =

    StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE); @Benchmark public Class<?> caller_class() throws Exception { return doCall(() -> STACK_WALKER.getCallerClass(), stackDepth); } @Benchmark public int stack_depth() throws Exception { return doCall( () -> STACK_WALKER.walk(stream -> stream.count()).intValue(), stackDepth); } @Benchmark public Object top_10_frames() throws Exception { return doCall( () -> STACK_WALKER.walk(s -> s.limit(10).collect(toList())), stackDepth); } }
  27. StackWalker Exception 1 31 8 30 5 30 Stack depth

    10 caller_class stack_depth top_10_frames Time, µs/op
  28. StackWalker Exception 1 105 29 105 5 105 Stack depth

    100 caller_class stack_depth top_10_frames Time, µs/op
  29. public class ArraysBenchmark { @Benchmark public int builtin() { return

    Arrays.mismatch(a, b); } @Benchmark public int handRolled() { byte[] a = this.a; byte[] b = this.b; int length = Math.min(a.length, b.length); for (int i = 0; i < length; i++) { if (a[i] != b[i]) { return i; } } return a.length == b.length ? -1 : length; } }
  30. Recap • 6 month release cycle • jlink and AOT

    help with footprint and startup performance • Compact Strings and optimized String concat make a huge difference • New APIs that improve performance of different tasks (StackWalker, Arrays.mismatch(), etc.)