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

Performance After Eight

Performance After Eight

Slides for the talk "Performance After Eight" given at Voxxed Days Zurich 2018

Dmitry Vyazelenko

March 08, 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 • Java’s new release cadence • 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. Modules JEP 200: The Modular JDK JEP 201: Modular Source

    Code JEP 220: Modular Run-Time Images JEP 261: Module System JEP 275: Modular Java Application Packaging JEP 282: jlink: The Java Linker JEP 295: Ahead-of-Time Compilation
  4. Footprint Download size, MB Installed size, MB jdk1.8.0_162 166 395

    jre1.8.0_162 60 206 jdk-9.0.4 305 577 jre-9.0.4 60 237
  5. sudo perf stat -r50 "$@" Elapsed time, msec $JDK8/java -cp

    55 $JDK8/java -jar 60 $JDK9/java -XX:+UseParallelGC -cp 81 $JDK9/java -XX:+UseParallelGC -jar 91
  6. sudo perf stat -r50 "$@" Elapsed time, msec $JDK8/java -cp

    55 $JDK8/java -jar 60 $JDK9/java -XX:+UseParallelGC -cp 81 $JDK9/java -XX:+UseParallelGC -jar 91 $JDK9/java -XX:+UseParallelGC --module-path mods -m 122 $JDK9/java -XX:+UseParallelGC -p modlibs -m 121
  7. sudo perf stat -r50 "$@" Elapsed time, msec $JDK8/java -cp

    55 $JDK8/java -jar 60 $JDK9/java -XX:+UseParallelGC -cp 81 $JDK9/java -XX:+UseParallelGC -jar 91 $JDK9/java -XX:+UseParallelGC --module-path mods -m 122 $JDK9/java -XX:+UseParallelGC -p modlibs -m 121 helloapp/bin/java -XX:+UseParallelGC -m 84
  8. sudo perf stat -r50 "$@" Elapsed time, msec $JDK8/java -cp

    55 $JDK8/java -jar 60 $JDK9/java -XX:+UseParallelGC -cp 81 $JDK9/java -XX:+UseParallelGC -jar 91 $JDK9/java -XX:+UseParallelGC --module-path mods -m 122 $JDK9/java -XX:+UseParallelGC -p modlibs -m 121 helloapp/bin/java -XX:+UseParallelGC -m 84 $JDK9/java -XX:AOTLibrary=./aot/hello.so -XX:+UseParallelGC -m 124 $JDK9/java -XX:AOTLibrary=./aot/hello.so,./aot/libjava.base.so ... 135
  9. sudo perf stat -r50 "$@" Elapsed time, msec $JDK8/java -cp

    55 $JDK8/java -jar 60 $JDK9/java -XX:+UseParallelGC -cp 81 $JDK9/java -XX:+UseParallelGC -jar 91 $JDK9/java -XX:+UseParallelGC --module-path mods -m 122 $JDK9/java -XX:+UseParallelGC -p modlibs -m 121 helloapp/bin/java -XX:+UseParallelGC -m 84 $JDK9/java -XX:AOTLibrary=./aot/hello.so -XX:+UseParallelGC -m 124 $JDK9/java -XX:AOTLibrary=./aot/hello.so,./aot/libjava.base.so ... 135 $JDK9/java -XX:+UseParallelGC -cp -Xshare:on 53
  10. sudo perf stat -r50 "$@" Elapsed time, msec $JDK8/java -cp

    55 $JDK8/java -jar 60 $JDK9/java -XX:+UseParallelGC -cp 81 $JDK9/java -XX:+UseParallelGC -jar 91 $JDK9/java -XX:+UseParallelGC --module-path mods -m 122 $JDK9/java -XX:+UseParallelGC -p modlibs -m 121 helloapp/bin/java -XX:+UseParallelGC -m 84 $JDK9/java -XX:AOTLibrary=./aot/hello.so -XX:+UseParallelGC -m 124 $JDK9/java -XX:AOTLibrary=./aot/hello.so,./aot/libjava.base.so ... 135 $JDK9/java -XX:+UseParallelGC -cp -Xshare:on 53 $JDK8/java -cp -Xshare:on 37
  11. 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.
  12. • Default GC changed • You are going to need

    JDK 10 (see [6] Why you want to run Java 10 if you're using the G1 Garbage Collector) • Here be dragons (see [5] What a difference a JVM makes? )
  13. 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()); } }
  14. 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 java.lang.String@3d82c5f3d footprint: COUNT AVG SUM DESCRIPTION 1 912 912 [C 1 24 24 java.lang.String 2 936 (total)
  15. JDK 9 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 java.lang.String@5e57643ed footprint: COUNT AVG SUM DESCRIPTION 1 464 464 [B 1 24 24 java.lang.String 2 488 (total)
  16. public class StringBasicBenchmark { @Benchmark public void charAt(Blackhole bh) {

    String v = value; for (int i = 0, len = v.length(); i < len; i++) { bh.consume(v.charAt(i)); } } @Benchmark public String toLowerCase() { return value.toLowerCase(Locale.US); } @Benchmark public String substringHead() { return value.substring(0, substringIndex); } @Benchmark public String substringTail() { return value.substring(substringIndex); } }
  17. Time, ns/op Allocations, B/op Benchmark (value) JDK 8 JDK 9

    JDK 8 JDK 9 StringBasicBenchmark.charAt Java is awesome! 61.7 57.7 0 0 StringBasicBenchmark.charAt Java ᛠwesome! 60.0 58.4 0 0 StringBasicBenchmark.substringHead Java is awesome! 11.2 10.6 48 48 StringBasicBenchmark.substringHead Java ᛠwesome! 11.3 10.9 48 48 StringBasicBenchmark.substringTail Java is awesome! 13.9 12.6 64 56 StringBasicBenchmark.substringTail Java ᛠwesome! 13.3 18.8 64 96 StringBasicBenchmark.toLowerCase Java is awesome! 47.5 28.3 120 56 StringBasicBenchmark.toLowerCase Java ᛠwesome! 104.4 99.5 120 176
  18. public class StringExtendedBenchmark { @Benchmark public int indexOf() { return

    value.indexOf(indexOf); } @Benchmark public boolean startsWith() { return value.startsWith(startsWith); } @Benchmark public boolean equals() { return value.equals(value2); } @Benchmark public int compareTo() { return value.compareTo(value2); } }
  19. Time, ns/op Benchmark (value) (value2) JDK 8 JDK 9 StringExtendedBenchmark.indexOf

    Java is awesome! Java is awesome! 7.1 7.4 StringExtendedBenchmark.indexOf Java is awesome! Java ᛠwesome! 7.7 2.4 StringExtendedBenchmark.indexOf Java ᛠwesome! Java is awesome! 7.8 8.3 StringExtendedBenchmark.indexOf Java ᛠwesome! Java ᛠwesome! 7.1 7.3 StringExtendedBenchmark.startsWith Java is awesome! Java is awesome! 6.1 6.2 StringExtendedBenchmark.startsWith Java is awesome! Java ᛠwesome! 6.1 6.2 StringExtendedBenchmark.startsWith Java ᛠwesome! Java is awesome! 6.0 6.4 StringExtendedBenchmark.startsWith Java ᛠwesome! Java ᛠwesome! 6.1 6.4 StringExtendedBenchmark.compareTo Java is awesome! Java is awesome! 6.8 5.7 StringExtendedBenchmark.compareTo Java is awesome! Java ᛠwesome! 5.6 6.1 StringExtendedBenchmark.compareTo Java ᛠwesome! Java is awesome! 5.6 6.5 StringExtendedBenchmark.compareTo Java ᛠwesome! Java ᛠwesome! 6.8 7.3 StringExtendedBenchmark.equals Java is awesome! Java is awesome! 4.6 6.2 StringExtendedBenchmark.equals Java is awesome! Java ᛠwesome! 4.5 2.7 StringExtendedBenchmark.equals Java ᛠwesome! Java is awesome! 4.4 2.7 StringExtendedBenchmark.equals Java ᛠwesome! Java ᛠwesome! 4.6 5.0
  20. 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)
  21. 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 + ")"); } }
  22. 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
  23. JDK 9 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)
  24. public class StringConcatBenchmark { private long currentTime; private int count;

    private String message; @Setup public void setup() { currentTime = System.currentTimeMillis(); count = 42; message = "Log payload"; } @Benchmark public String log() { return "[" + currentTime + "]: " + message + " (" + count + ")"; } }
  25. StringConcatBenchmark.log Time, ns/op Allocations, B/op JDK 8 101.2 272 JDK

    9 36.4 80 JDK 9: -XX:-CompactStrings 38.7 112 JDK 9: -Djava.lang.invoke.stringConcat=BC_SB 71.7 192 JDK 9: -Djava.lang.invoke.stringConcat=BC_SB_SIZED 62.1 176 JDK 9: -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT 52.9 160 JDK 9: -Djava.lang.invoke.stringConcat=MH_SB_SIZED 65.4 176 JDK 9: -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT 66.3 216
  26. 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(); } }
  27. Time, us/op Allocations, B/op Benchmark JDK 8 JDK 9 JDK

    8 JDK 9 StringConcatLoopBenchmark.naive 36.2 5.2 384288 52880 StringConcatLoopBenchmark.proper 6.9 2.9 26496 10328 5x 1.8x 15x 5x
  28. public class LayoutIntrospection { public static void main(String[] args) {

    footprint(List.of()); footprint(List.of(1)); footprint(List.of(1, 2)); ... footprint(Set.of(1)); footprint(Set.of(1, 2)); ... footprint(Map.of(1, 1)); footprint(Map.of(1, 1, 2, 2)); footprint(Map.ofEntries(Map.entry(1, 1), Map.entry(2, 2))); } }
  29. Footprint, B Payload, B Overhead, % List.of() 16 0 -

    Collections.emptyList() 16 0 - List.of(1) 40 16 60 Collections.singletonList(1) 40 16 60 new ArrayList<>(1) 64 16 75 new LinkedList<>(1) 72 16 78 List.of(1,2) 56 32 43 new ArrayList<>(1,2) 80 32 60 new LinkedList<>(1,2) 112 32 71 List.of(1..10) 240 160 33 new ArrayList<>(1..10) 240 160 33 new LinkedList<>(1..10) 432 160 63
  30. Footprint, B Payload, B Overhead, % Set.of() 16 0 -

    Collections.emptySet() 16 0 - Set.of(1) 32 16 50 Collections.singleton(1) 32 16 50 new HashSet<>(1) 208 16 92 new LinkedHashSet<>(1) 224 16 93 new TreeSet<>(1) 136 16 88 Set.of(1,2) 56 32 43 new HashSet<>(1,2) 256 32 88 new LinkedHashSet<>(1,2) 280 32 89 new TreeSet<>(1,2) 192 32 83 Set.of(1..10) 280 160 43 new HashSet<>(1..10) 640 160 75 new LinkedHashSet<>(1..10) 792 160 80 new TreeSet<>(1..10) 640 160 75
  31. Footprint, B Payload, B Overhead, % Map.of() 24 0 -

    Collections.emptyMap() 24 0 - Map.of(1,1) 48 16 67 Collections.singletonMap(1,1) 56 16 71 new HashMap<>(1,1) 120 16 87 new LinkedHashMap<>(1,1) 136 16 88 new TreeMap<>(1,1) 104 16 85 Map.of(1,1,2,2) 112 32 71 new HashMap<>(1,1,2,2) 176 32 82 new LinkedHashMap<>(1,1,2,2) 200 32 84 new TreeMap<>(1,1,2,2) 160 32 80 Map.of(1,1..10,10) 368 160 57 new HashMap<>(1,1..10,10) 608 160 74 new LinkedHashMap<>(1,1..10,10) 696 160 77 new TreeMap<>(1,1..10,10) 608 160 74 Map.ofEntries(collidingEntries(16)) 816 512 37 new HashMap<>(collidingEntries(16)) 1728 512 70 new LinkedHashMap<>(collidingEntries(16)) 1736 512 71 new TreeMap<>(collidingEntries(16)) 1200 512 57
  32. public class ListBenchmark extends AbstractCollectionBenchmark { @Benchmark public Object get()

    { return list.get(index); } @Benchmark public Object contains() { return list.contains(contains); } @Benchmark public void iterator(Blackhole bh) { List<Object> list = this.list; for (Object o : list) { bh.consume(o); } } }
  33. Benchmark (kind) (size) Time, ns/op ListBenchmark.contains List.of 1 3.5 ListBenchmark.contains

    List.of 2 4.1 ListBenchmark.contains List.of 10 8.6 ListBenchmark.contains List.of 100 34.8 ListBenchmark.contains ArrayList 1 4.1 ListBenchmark.contains ArrayList 2 5.2 ListBenchmark.contains ArrayList 10 8.7 ListBenchmark.contains ArrayList 100 33.7 ListBenchmark.contains LinkedList 1 3.9 ListBenchmark.contains LinkedList 2 5.1 ListBenchmark.contains LinkedList 10 10.1 ListBenchmark.contains LinkedList 100 97.2
  34. public class SetBenchmark extends AbstractCollectionBenchmark { @Benchmark public Object contains()

    { return set.contains(contains); } @Benchmark public void iterator(Blackhole bh) { Set<Object> list = this.set; for (Object o : list) { bh.consume(o); } } }
  35. Benchmark (kind) (size) Time, ns/op SetBenchmark.contains Set.of 1 3.5 SetBenchmark.contains

    Set.of 2 3.6 SetBenchmark.contains Set.of 10 11.8 SetBenchmark.contains Set.of 100 12.1 SetBenchmark.contains HashSet 1 6.8 SetBenchmark.contains HashSet 2 6.9 SetBenchmark.contains HashSet 10 7.3 SetBenchmark.contains HashSet 100 8.1 SetBenchmark.contains LinkedHashSet 1 6.8 SetBenchmark.contains LinkedHashSet 2 6.8 SetBenchmark.contains LinkedHashSet 10 6.9 SetBenchmark.contains LinkedHashSet 100 8.1 SetBenchmark.contains TreeSet 1 4.3 SetBenchmark.contains TreeSet 2 4.8 SetBenchmark.contains TreeSet 10 7.1 SetBenchmark.contains TreeSet 100 13.5
  36. public class ExceptionBenchmark { @Benchmark public Class<?> callerClass() throws Exception

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

    StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE); @Benchmark public Class<?> callerClass() throws Exception { return doCall(() -> STACK_WALKER.getCallerClass(), stackDepth); } @Benchmark public int stackDepth() throws Exception { return doCall( () -> STACK_WALKER.walk(stream -> stream.count()).intValue(), stackDepth); } @Benchmark public Object top10frames() throws Exception { return doCall( () -> STACK_WALKER.walk(s -> s.limit(10).collect(toList())), stackDepth); } }
  38. Benchmark (stackSize) Time, us/op ExceptionBenchmark.callerClass 10 16.6 ExceptionBenchmark.callerClass 100 54.1

    ExceptionBenchmark.stackDepth 10 16.0 ExceptionBenchmark.stackDepth 100 53.8 ExceptionBenchmark.top10frames 10 16.3 ExceptionBenchmark.top10frames 100 54.3 StackWalkerBenchmark.callerClass 10 0.7 StackWalkerBenchmark.callerClass 100 0.8 StackWalkerBenchmark.stackDepth 10 7.8 StackWalkerBenchmark.stackDepth 100 30.3 StackWalkerBenchmark.top10frames 10 5.2 StackWalkerBenchmark.top10frames 100 5.2
  39. java.lang.Math#fma(double, double, double) java.lang.Math#fma(float, float, float) java.util.Objects#checkIndex(int, int) java.util.Objects#checkFromToIndex(int, int,

    int) java.util.Objects#checkFromIndexSize(int, int, int) java.util.Arrays#compare(int[], int[]) java.util.Arrays#compare(int[], int, int, int[], int, int) java.util.Arrays#compareUnsigned(int[], int[]) java.util.Arrays#compareUnsigned(int[], int, int, int[], int, int) java.util.Arrays#equals(int[], int, int, int[], int, int) java.util.Arrays#mismatch(int[], int[]) java.util.Arrays#mismatch(int[], int, int, int[], int, int) // + for all primitive types + Object[]
  40. 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; } }
  41. Benchmark (prefixSize) Time, ns/op ArraysBenchmark.handRolled 10 6.8 ArraysBenchmark.handRolled 100 32.7

    ArraysBenchmark.handRolled 1000 295.5 ArraysBenchmark.handRolled 10000 2925.3 ArraysBenchmark.builtin 10 6.4 ArraysBenchmark.builtin 100 8.5 ArraysBenchmark.builtin 1000 24.7 ArraysBenchmark.builtin 10000 231.8
  42. Recap • New Java release every 6 month • Not

    all releases created equal: LTS vs non-LTS • JDK 9 has lots of exciting performance features • JDK 10 is bringing even more
  43. References 1) https://github.com/vyazelenko/performance-after-eight 2) http://openjdk.java.net/projects/jdk9/ 3) http://openjdk.java.net/projects/jdk/10/ 4) https://www.azul.com/java-stable-secure-free-choose-two-three/ 5)

    http://psy-lob-saw.blogspot.ch/2018/01/what-diference-jvm-makes.html 6) https://www.opsian.com/blog/java-10-with-g1/ 7) http://openjdk.java.net/projects/code-tools/jmh/ 8) http://openjdk.java.net/projects/code-tools/jol/