String Concatenation de 1 à 9

5ce0156bc2f1864459fd77229eff3fe3?s=47 forax
April 21, 2016

String Concatenation de 1 à 9

Ce que Java 9 change à la concatenation de chaine de caractères et pourquoi ?

5ce0156bc2f1864459fd77229eff3fe3?s=128

forax

April 21, 2016
Tweet

Transcript

  1. #DevoxxFR Rémi Forax Université Paris Est Marne la vallée String

    concatenation from 1 to 9
  2. #DevoxxFR Me, Myself and I Maitre de conférence University Paris

    Est Marne la vallée Developer: OpenJDK, ASM, Tatoo, Dragon, … Java Community Process Expert – Invokedynamic (JSR 292) – Lambda (JSR 335 + java.util/java.util.stream) – Jigsaw (JSR 376) 2
  3. #DevoxxFR 3 public class Hello { private static String hello(String

    text, int value) { return "hello" + text + value; } public static void main(String[] args) { System.out.println(hello(" devoxx ", 2016)); } } $ java Hello hello devoxx 2016 Hello Devoxx
  4. #DevoxxFR 4 $ /usr/jdk/jdk1.8.0/bin/javap -c -private bin/Hello.class private static java.lang.String

    hello(java.lang.String, int) 0: new #16 // class java/lang/StringBuilder 3: dup 4: ldc #18 // String hello 6: invokespecial #19 // Method j/l/StringBuilder."<init>":(Lj/l/String;)V 9: aload_0 10: invokevirtual #22 // Method j/l/StringBuilder.append:(Lj/l/String;)L... 13: iload_1 14: invokevirtual #26 // Method j/l/StringBuilder.append:(I)Lj/l/StringBuilder; 17: invokevirtual #29 // Method j/l/StringBuilder.toString:()Lj/l/String; 20: areturn javap on 1.8
  5. #DevoxxFR 5 public class Hello { private static String hello(String

    text, int value) { return new StringBuilder("hello") .append(text) .append(value) .toString(); } public static void main(String[] args) { System.out.println(hello(" devoxx ", 2016)); } } from javap
  6. #DevoxxFR 6 /usr/jdk/jdk1.8.0/bin/java -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Hello ... 64 15

    Hello::hello (21 bytes) @ 13 java.lang.String::<init> (82 bytes) inline (hot) @ 1 java.lang.Object::<init> (1 bytes) inline (hot) @ 75 java.util.Arrays::copyOfRange (63 bytes) inline (hot) @ 54 java.lang.Math::min (11 bytes) (intrinsic) @ 57 java.lang.System::arraycopy (0 bytes) (intrinsic) @ 6 java.lang.StringBuilder::<init> (18 bytes) inline (hot) @ 10 java.lang.StringBuilder::append (8 bytes) inline (hot) @ 14 java.lang.StringBuilder::append (8 bytes) inline (hot) @ 17 java.lang.StringBuilder::toString (17 bytes) inline (hot) What the JIT does ?
  7. #DevoxxFR # {method} {0x00007f8a0bc00388} 'hello' '(Ljava/lang/String;I)Ljava/lang/String;' in 'Hello' # parm0:

    rsi:rsi = 'java/lang/String' # parm1: rdx = int # [sp+0x40] (sp of caller) 0x00007f8a1907a6a0: mov %eax,-0x14000(%rsp) 0x00007f8a1907a6a7: push %rbp 0x00007f8a1907a6a8: sub $0x30,%rsp ;*synchronization entry ; - Hello::hello@-1 (line 3) 0x00007f8a1907a6ac: mov %rsi,%rcx 0x00007f8a1907a6af: test %rsi,%rsi 0x00007f8a1907a6b2: jne 0x00007f8a1907a9b5 0x00007f8a1907a6b8: mov $0x76d103fb0,%rbp ; {oop("null")} 0x00007f8a1907a6c2: mov 0xc(%rbp),%r10d 0x00007f8a1907a6c6: mov 0xc(%r12,%r10,8),%esi 0x00007f8a1907a6cb: mov %esi,%r10d 0x00007f8a1907a6ce: add $0x5,%r10d 0x00007f8a1907a6d2: test %r10d,%r10d 0x00007f8a1907a6d5: jl 0x00007f8a1907a99a 0x00007f8a1907a6db: mov %edx,%r13d 0x00007f8a1907a6de: neg %r13d 0x00007f8a1907a6e1: mov %edx,%r8d 0x00007f8a1907a6e4: cmp $0x80000000,%edx 0x00007f8a1907a6ea: jne 0x00007f8a1907a91b 0x00007f8a1907a6f0: xor %r14d,%r14d 0x00007f8a1907a6f3: mov $0xb,%ebx 0x00007f8a1907a6f8: jmp 0x00007f8a1907a712 0x00007f8a1907a6fa: nopw 0x0(%rax,%rax,1) 0x00007f8a1907a700: inc %r11d 0x00007f8a1907a703: mov 0x10(%rdi,%r11,4),%r9d 0x00007f8a1907a708: cmp %r9d,%r10d 0x00007f8a1907a70b: jg 0x00007f8a1907a700 0x00007f8a1907a70d: add %r11d,%ebx 0x00007f8a1907a710: inc %ebx 0x00007f8a1907a712: mov %ebx,%r10d 0x00007f8a1907a715: add %esi,%r10d 0x00007f8a1907a718: mov %r10d,%edx 0x00007f8a1907a71b: add $0x5,%edx 0x00007f8a1907a71e: test %edx,%edx 0x00007f8a1907a720: jl 0x00007f8a1907a99d 0x00007f8a1907a726: cmp $0x80000,%edx 0x00007f8a1907a72c: ja 0x00007f8a1907a95e 0x00007f8a1907a732: mov 0x60(%r15),%r9 0x00007f8a1907a736: movslq %r10d,%r10 0x00007f8a1907a739: shl %r10 0x00007f8a1907a73c: add $0x21,%r10 0x00007f8a1907a740: and $0xfffffffffffffff8,%r10 0x00007f8a1907a744: mov %r9,%r11 0x00007f8a1907a747: add %r10,%r11 0x00007f8a1907a74a: cmp 0x70(%r15),%r11 0x00007f8a1907a74e: jae 0x00007f8a1907a95e 0x00007f8a1907a754: mov %r11,0x60(%r15) 0x00007f8a1907a758: prefetchw 0xc0(%r11) 0x00007f8a1907a760: movq $0x1,(%r9) 0x00007f8a1907a767: prefetchw 0x100(%r11) 0x00007f8a1907a76f: movl $0xf8000041,0x8(%r9) ; {metadata({type array char})} 0x00007f8a1907a777: mov %edx,0xc(%r9) 0x00007f8a1907a77b: prefetchw 0x140(%r11) 0x00007f8a1907a783: prefetchw 0x180(%r11) 7 What does the JIT generate ? 0x00007f8a1907a78b: mov %rcx,(%rsp) 0x00007f8a1907a78f: mov %r8d,0x8(%rsp) 0x00007f8a1907a794: mov 0xc(%rbp),%r10d 0x00007f8a1907a798: mov %r9,%rsi 0x00007f8a1907a79b: add $0x1a,%rsi 0x00007f8a1907a79f: mov 0xc(%r12,%r10,8),%ebp 0x00007f8a1907a7a4: lea (%r12,%r10,8),%r11 0x00007f8a1907a7a8: lea 0x10(%r12,%r10,8),%rdi 0x00007f8a1907a7ad: movslq %ebp,%r10 0x00007f8a1907a7b0: mov %r10,0x18(%rsp) 0x00007f8a1907a7b5: mov $0x68,%r10d 0x00007f8a1907a7bb: mov %r10w,0x10(%r9) 0x00007f8a1907a7c0: mov $0x6f,%r8d 0x00007f8a1907a7c6: mov $0x65,%r11d 0x00007f8a1907a7cc: mov %r11w,0x12(%r9) 0x00007f8a1907a7d1: mov $0x6c,%r10d 0x00007f8a1907a7d7: mov %r10w,0x14(%r9) 0x00007f8a1907a7dc: mov %r10w,0x16(%r9) 0x00007f8a1907a7e1: mov %r8w,0x18(%r9) 0x00007f8a1907a7e6: mov %r9,0x10(%rsp) 0x00007f8a1907a7eb: mov 0x18(%rsp),%rdx 0x00007f8a1907a7f0: mov $0x7f8a19052300,%r10 0x00007f8a1907a7fa: callq *%r10 0x00007f8a1907a7fd: mov 0x8(%rsp),%r10d 0x00007f8a1907a802: cmp $0x80000000,%r10d 0x00007f8a1907a809: je 0x00007f8a1907a81c 0x00007f8a1907a80b: mov 0x8(%rsp),%r8d 0x00007f8a1907a810: test %r10d,%r10d 0x00007f8a1907a813: jl 0x00007f8a1907a852 0x00007f8a1907a815: mov 0x8(%rsp),%r13d 0x00007f8a1907a81a: jmp 0x00007f8a1907a858 0x00007f8a1907a81c: mov 0x18(%rsp),%r10 0x00007f8a1907a821: mov 0x10(%rsp),%r11 0x00007f8a1907a826: lea 0x1a(%r11,%r10,2),%rsi 0x00007f8a1907a82b: mov $0xb,%edx 0x00007f8a1907a830: mov $0x76d103ff8,%rdi ; {oop([C)} 0x00007f8a1907a83a: add $0x10,%rdi 0x00007f8a1907a83e: mov $0x7f8a19052300,%r10 0x00007f8a1907a848: callq *%r10 0x00007f8a1907a84b: mov 0x8(%rsp),%r8d 0x00007f8a1907a850: jmp 0x00007f8a1907a8b5 0x00007f8a1907a852: mov $0x2d,%r14d 0x00007f8a1907a858: add %ebp,%ebx 0x00007f8a1907a85a: add $0x5,%ebx 0x00007f8a1907a85d: mov %r13d,%r11d 0x00007f8a1907a860: sar $0x1f,%r11d 0x00007f8a1907a864: movslq %r13d,%r10 0x00007f8a1907a867: imul $0x66666667,%r10,%r10 0x00007f8a1907a86e: sar $0x22,%r10 0x00007f8a1907a872: mov %r10d,%r10d 0x00007f8a1907a875: mov %r10d,%edx 0x00007f8a1907a878: sub %r11d,%edx 0x00007f8a1907a87b: mov %edx,%r9d 0x00007f8a1907a87e: shl $0x3,%r9d 0x00007f8a1907a882: mov %edx,%ecx 0x00007f8a1907a884: shl %ecx 0x00007f8a1907a886: add %r9d,%ecx 0x00007f8a1907a889: sub %ecx,%r13d 0x00007f8a1907a88c: add $0x30,%r13d 0x00007f8a1907a890: mov 0x10(%rsp),%r9 0x00007f8a1907a895: mov %r13w,0xe(%r9,%rbx,2) 0x00007f8a1907a89b: cmp %r11d,%r10d 0x00007f8a1907a89e: je 0x00007f8a1907a8a7 0x00007f8a1907a8a0: dec %ebx 0x00007f8a1907a8a2: mov %edx,%r13d 0x00007f8a1907a8a5: jmp 0x00007f8a1907a85d 0x00007f8a1907a8a7: test %r14d,%r14d 0x00007f8a1907a8aa: je 0x00007f8a1907a8b5 0x00007f8a1907a8ac: add $0xfffffffffffffffe,%ebx 0x00007f8a1907a8af: mov %r14w,0x10(%r9,%rbx,2) 0x00007f8a1907a8b5: mov 0x60(%r15),%rax 0x00007f8a1907a8b9: mov %rax,%r10 0x00007f8a1907a8bc: add $0x18,%r10 0x00007f8a1907a8c0: cmp 0x70(%r15),%r10 0x00007f8a1907a8c4: jae 0x00007f8a1907a943 0x00007f8a1907a8c6: mov %r10,0x60(%r15) 0x00007f8a1907a8ca: prefetchw 0xc0(%r10) 0x00007f8a1907a8d2: mov $0xf80002da,%r11d ; {metadata('java/lang/String')} 0x00007f8a1907a8d8: mov $0x0,%r10 0x00007f8a1907a8e2: lea (%r10,%r11,8),%r10 0x00007f8a1907a8e6: mov 0xa8(%r10),%r10 0x00007f8a1907a8ed: mov %r10,(%rax) 0x00007f8a1907a8f0: movl $0xf80002da,0x8(%rax) ; {metadata('java/lang/String')} 0x00007f8a1907a8f7: mov %r12d,0xc(%rax) 0x00007f8a1907a8fb: mov %r12,0x10(%rax) 0x00007f8a1907a8ff: mov 0x10(%rsp),%r10 0x00007f8a1907a904: mov %r10,%r11 0x00007f8a1907a907: shr $0x3,%r11 0x00007f8a1907a90b: mov %r11d,0xc(%rax) 0x00007f8a1907a90f: add $0x30,%rsp 0x00007f8a1907a913: pop %rbp 0x00007f8a1907a914: test %eax,0xa2a36e6(%rip) # 0x00007f8a2331e000 ; {poll_return} 0x00007f8a1907a91a: retq /usr/jdk/jdk1.8.0/bin/java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly Hello
  8. #DevoxxFR 8 -XX:+OptimizeStringConcat "hello" " devoxx " 2016

  9. #DevoxxFR 9 -XX:+OptimizeStringConcat "hello" " devoxx " 2016 5 s.array.arraylength

    Integer.stringsize(i) + + size =
  10. #DevoxxFR -XX:+OptimizeStringConcat "hello" " devoxx " 2016 5 s.array.arraylength Integer.stringsize(i)

    + + new char[size] size = 10
  11. #DevoxxFR -XX:+OptimizeStringConcat "hello" " devoxx " 2016 5 s.array.arraylength Integer.stringsize(i)

    + + new char[size] size = 2 0 1 6 Integer.getChars(i, ...) 11
  12. #DevoxxFR 1 2 -XX:+OptimizeStringConcat "hello" " devoxx " 2016 5

    s.array.arraylength Integer.stringsize(i) + + new char[size] size = d e v o x x 2 0 1 6 String.getChars(i, ...) 12
  13. #DevoxxFR 1 3 -XX:+OptimizeStringConcat "hello" " devoxx " 2016 5

    s.array.arraylength Integer.stringsize(i) + + new char[size] size = h e l l o d e v o x x 2 0 1 6 5 movs 13
  14. #DevoxxFR 14 "hello" + text + value; is transformed by

    the compiler to new StringBuilder("hello") .append(text) .append(value) .toString(); Summary
  15. #DevoxxFR 15 "hello" + text + value; is transformed by

    the compiler to new StringBuilder("hello") .append(text) .append(value) .toString(); is transformed by the JIT to int size = 5 + text.length() + Integer.stringsize(value) char[] data = new char[size]; // almost see later Integer.getChars(i, size, data); System.arraycopy(text.data, 0, data, 5, text.length()); data[0] = 'h'; data[1] = 'e'; data[2] = 'l'; // etc new String(data); Summary
  16. #DevoxxFR 16 -XX:+OptimizeStringConcat is very powerful

  17. #DevoxxFR 17 public class Benchmark { private int x; String

    concat_pre() { return "" + x; } String concat_post() { return x + ""; } String integer_toString() { return Integer.toString(x); } String string_valueOf() { return String.valueOf(x); } } see http://stackoverflow.com/questions/4105331/how-to-convert-from-int-to-string/ Which one is the fastest ?
  18. #DevoxxFR 18 WTF ? Data from Aleksey Shipilev

  19. #DevoxxFR 19 But relying on -XX:+OptimizeStringConcat can be tricky

  20. #DevoxxFR 20 public class Hello { private static String hello(StringObject

    text, int value) { return "hello" + text + value; } public static void main(String[] args) { System.out.println(hello(" devoxx ", 2016)); } } $ java Hello hello devoxx 2016 What about ?
  21. #DevoxxFR # {method} {0x00007fae2444a388} 'hello' '(Ljava/lang/Object;I)Ljava/lang/String;' in 'Hello' # parm0:

    rsi:rsi = 'java/lang/Object' # parm1: rdx = int # [sp+0x50] (sp of caller) 0x00007fae1d077980: mov %eax,-0x14000(%rsp) 0x00007fae1d077987: push %rbp 0x00007fae1d077988: sub $0x40,%rsp ;*synchronization entry ; - Hello::hello@-1 (line 3) 0x00007fae1d07798c: mov %edx,(%rsp) 0x00007fae1d07798f: mov %rsi,%rbp 0x00007fae1d077992: mov 0x60(%r15),%rbx 0x00007fae1d077996: mov %rbx,%r10 0x00007fae1d077999: add $0x40,%r10 0x00007fae1d07799d: cmp 0x70(%r15),%r10 0x00007fae1d0779a1: jae 0x00007fae1d077e16 0x00007fae1d0779a7: mov %r10,0x60(%r15) 0x00007fae1d0779ab: prefetchw 0xc0(%r10) 0x00007fae1d0779b3: movq $0x1,(%rbx) 0x00007fae1d0779ba: prefetchw 0x100(%r10) 0x00007fae1d0779c2: movl $0xf8000041,0x8(%rbx) ; {metadata({type array char})} 0x00007fae1d0779c9: prefetchw 0x140(%r10) 0x00007fae1d0779d1: movl $0x15,0xc(%rbx) 0x00007fae1d0779d8: prefetchw 0x180(%r10) 0x00007fae1d0779e0: mov %r12,0x10(%rbx) 0x00007fae1d0779e4: mov %r12,0x18(%rbx) 0x00007fae1d0779e8: mov %r12,0x20(%rbx) 0x00007fae1d0779ec: mov %r12,0x28(%rbx) 0x00007fae1d0779f0: mov %r12,0x30(%rbx) 0x00007fae1d0779f4: mov %r12,0x38(%rbx) 0x00007fae1d0779f8: mov %rbx,%r10 0x00007fae1d0779fb: add $0x10,%r10 0x00007fae1d0779ff: mov %r10,0x28(%rsp) 0x00007fae1d077a04: mov $0x76d15b320,%rdi ; {oop([C)} 0x00007fae1d077a0e: add $0x10,%rdi 0x00007fae1d077a12: mov $0x5,%edx 0x00007fae1d077a17: mov %r10,%rsi 0x00007fae1d077a1a: mov $0x7fae1d052300,%r10 0x00007fae1d077a24: callq *%r10 ;*invokespecial <init> ; - Hello::hello@6 (line 3) 0x00007fae1d077a27: mov %rbp,%r10 0x00007fae1d077a2a: mov 0x8(%r10),%r8d ; implicit exception: dispatches to 0x00007fae1d078159 0x00007fae1d077a2e: cmp $0xf80002da,%r8d ; {metadata('java/lang/String')} 0x00007fae1d077a35: jne 0x00007fae1d077ffa ;*invokevirtual toString ; - java.lang.String::valueOf@10 (line 2994) ; - java.lang.StringBuilder::append@2 (line 131) ; - Hello::hello@10 (line 3) 0x00007fae1d077a3b: mov 0xc(%r10),%r8d ;*getfield value ; - java.lang.String::length@1 (line 623) ; - java.lang.AbstractStringBuilder::append@10 (line 420) ; - java.lang.StringBuilder::append@2 (line 136) ; - java.lang.StringBuilder::append@5 (line 131) ; - Hello::hello@10 (line 3) 0x00007fae1d077a3f: mov 0xc(%r12,%r8,8),%ebp ;*arraylength ; - java.lang.String::length@4 (line 623) ; - java.lang.AbstractStringBuilder::append@10 (line 420) ; - java.lang.StringBuilder::append@2 (line 136) ; - java.lang.StringBuilder::append@5 (line 131) 21 What does the JIT generate ? 0x00007fae1d077a44: mov %ebp,%r11d 0x00007fae1d077a47: add $0xfffffffffffffff0,%r11d ;*isub ; - java.lang.AbstractStringBuilder::ensureCapacityInternal@6 ; - java.lang.AbstractStringBuilder::append@21 (line 421) ; - java.lang.StringBuilder::append@2 (line 136) ; - java.lang.StringBuilder::append@5 (line 131) ; - Hello::hello@10 (line 3) 0x00007fae1d077a4b: mov %ebp,%r9d 0x00007fae1d077a4e: add $0x5,%r9d ;*iadd ; - java.lang.AbstractStringBuilder::append@44 (line 423) ; - java.lang.StringBuilder::append@2 (line 136) ; - java.lang.StringBuilder::append@5 (line 131) ; - Hello::hello@10 (line 3) 0x00007fae1d077a52: mov %r9d,%r14d 0x00007fae1d077a55: test %r11d,%r11d 0x00007fae1d077a58: jg 0x00007fae1d078027 ;*ifle ; - java.lang.AbstractStringBuilder::ensureCapacityInternal@7 ; - java.lang.AbstractStringBuilder::append@21 (line 421) ; - java.lang.StringBuilder::append@2 (line 136) ; - java.lang.StringBuilder::append@5 (line 131) ; - Hello::hello@10 (line 3) 0x00007fae1d077a5e: lea (%r12,%r8,8),%rsi ;*getfield value ; - java.lang.String::length@1 (line 623) ; - java.lang.AbstractStringBuilder::append@10 (line 420) ; - java.lang.StringBuilder::append@2 (line 136) ; - java.lang.StringBuilder::append@5 (line 131) ; - Hello::hello@10 (line 3) 0x00007fae1d077a62: cmp $0x15,%r9d 0x00007fae1d077a66: ja 0x00007fae1d077f35 0x00007fae1d077a6c: test %ebp,%ebp 0x00007fae1d077a6e: jle 0x00007fae1d077a8c 0x00007fae1d077a70: lea 0x10(%r12,%r8,8),%rdi 0x00007fae1d077a75: mov %rbx,%rsi 0x00007fae1d077a78: add $0x1a,%rsi 0x00007fae1d077a7c: movslq %ebp,%rdx 0x00007fae1d077a7f: mov $0x7fae1d0523c0,%r10 0x00007fae1d077a89: callq *%r10 ;*invokestatic arraycopy ; - java.lang.String::getChars@58 (line 826) ; - java.lang.AbstractStringBuilder::append@35 (line 422) ; - java.lang.StringBuilder::append@2 (line 136) ; - java.lang.StringBuilder::append@5 (line 131) ; - Hello::hello@10 (line 3) 0x00007fae1d077a8c: mov (%rsp),%r9d 0x00007fae1d077a90: cmp $0x80000000,%r9d 0x00007fae1d077a97: je 0x00007fae1d07804d ;*if_icmpne ; - java.lang.AbstractStringBuilder::append@3 (line 640) ; - java.lang.StringBuilder::append@2 (line 208) ; - Hello::hello@14 (line 3) 0x00007fae1d077a9d: test %r9d,%r9d 0x00007fae1d077aa0: jl 0x00007fae1d078069 ;*ifge ; - java.lang.AbstractStringBuilder::append@16 (line 644) ; - java.lang.StringBuilder::append@2 (line 208) ; - Hello::hello@14 (line 3) 0x00007fae1d077aa6: mov $0x76d12cda8,%r11 ; {oop([I)} 0x00007fae1d077ab0: mov 0x10(%r11),%r10d 0x00007fae1d077ab4: cmp %r10d,%r9d 0x00007fae1d077ab7: jg 0x00007fae1d077ac0 0x00007fae1d077ab9: mov %ebp,%edi8 0x00007fae1d077abb: jmpq 0x00007fae1d077b71 0x00007fae1d077ac0: mov 0x14(%r11),%r10d 0x00007fae1d077ac4: cmp %r10d,%r9d 0x00007fae1d077ac7: jle 0x00007fae1d077b6a 0x00007fae1d077acd: mov 0x18(%r11),%r10d 0x00007fae1d077ad1: cmp %r10d,%r9d 0x00007fae1d077ad4: jg 0x00007fae1d077ae0 0x00007fae1d077ad6: mov $0x2,%edi 0x00007fae1d077adb: jmpq 0x00007fae1d077b6f 0x00007fae1d077ae0: mov 0x1c(%r11),%r10d 0x00007fae1d077ae4: cmp %r10d,%r9d 0x00007fae1d077ae7: jg 0x00007fae1d077af3 0x00007fae1d077ae9: mov $0x3,%edi 0x00007fae1d077aee: jmpq 0x00007fae1d077b6f 0x00007fae1d077af3: mov 0x20(%r11),%r10d 0x00007fae1d077af7: cmp %r10d,%r9d 0x00007fae1d077afa: jg 0x00007fae1d077b03 0x00007fae1d077afc: mov $0x4,%edi 0x00007fae1d077b01: jmp 0x00007fae1d077b6f 0x00007fae1d077b03: mov 0x24(%r11),%r10d 0x00007fae1d077b07: cmp %r10d,%r9d 0x00007fae1d077b0a: jg 0x00007fae1d077b13 0x00007fae1d077b0c: mov $0x5,%edi 0x00007fae1d077b11: jmp 0x00007fae1d077b6f 0x00007fae1d077b13: mov 0x28(%r11),%r10d 0x00007fae1d077b17: cmp %r10d,%r9d 0x00007fae1d077b1a: jg 0x00007fae1d077b23 0x00007fae1d077b1c: mov $0x6,%edi 0x00007fae1d077b21: jmp 0x00007fae1d077b6f 0x00007fae1d077b23: mov 0x2c(%r11),%r10d 0x00007fae1d077b27: cmp %r10d,%r9d 0x00007fae1d077b2a: jg 0x00007fae1d077b33 0x00007fae1d077b2c: mov $0x7,%edi 0x00007fae1d077b31: jmp 0x00007fae1d077b6f 0x00007fae1d077b33: mov 0x30(%r11),%r10d 0x00007fae1d077b37: cmp %r10d,%r9d 0x00007fae1d077b3a: jg 0x00007fae1d077b43 0x00007fae1d077b3c: mov $0x8,%edi 0x00007fae1d077b41: jmp 0x00007fae1d077b6f 0x00007fae1d077b43: mov 0x34(%r11),%r10d 0x00007fae1d077b47: cmp %r10d,%r9d 0x00007fae1d077b4a: jg 0x00007fae1d078129 ;*if_icmpgt ; - java.lang.Integer::stringSize@8 (line 473) ; - java.lang.AbstractStringBuilder::append@30 (line 645) ; - java.lang.StringBuilder::append@2 (line 208) ; - Hello::hello@14 (line 3) 0x00007fae1d077b50: mov $0x9,%edi 0x00007fae1d077b55: jmp 0x00007fae1d077b6f 0x00007fae1d077b57: nopw 0x0(%rax,%rax,1) 0x00007fae1d077b60: mov %ecx,%eax 0x00007fae1d077b62: mov %r9d,%edx 0x00007fae1d077b65: jmpq 0x00007fae1d077bff 0x00007fae1d077b6a: mov $0x1,%edi • + 290 more lines /usr/jdk/jdk1.8.0/bin/java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly Hello
  22. #DevoxxFR 22 We fall out of the performance cliff :(

  23. #DevoxxFR 23 public class Hello { private static String hello(StringObject

    text, int value) { return new StringBuilder("hello") .append((text == null)? "null": text.toString()) .append(value) .toString(); } public static void main(String[] args) { System.out.println(hello(" devoxx ", 2016)); } } Why ? This is a polymorphic call and the OptimizeStringConcat is not able to handle that :(
  24. #DevoxxFR 24 All these expressions are not optimizable by OptimizeStringConcat

    ! long l = … new StringBuilder().append(l).toString() double d = … new StringBuilder().append(d).toString() new StringBuilder().append(value++).toString() In fact it's worst than that
  25. #DevoxxFR 25 How to solve our problem ?

  26. #DevoxxFR 26 public class Hello { private static String hello(StringObject

    text, int value) { String s = String.valueOf(text); return new StringBuilder("hello") .append(s) .append(value) .toString(); } public static void main(String[] args) { System.out.println(hello(" devoxx ", 2016)); } } Call toString() before ! ahah
  27. #DevoxxFR 27 Java is a dirty language toString can do

    side effect :(
  28. #DevoxxFR 28 public class Hello { private static String $$concat$$(String

    text, int value) { return new StringBuilder("hello") .append(text) .append(value) .toString(); } private static String hello(StringObject text, int value) { return $$concat$$(String.valueOf(text.toString())); } } Introduce a synthetic method ahah
  29. #DevoxxFR 29 But this add bloat to the class and

    change the stacktrace !
  30. #DevoxxFR 30 Use invokedynamic !

  31. #DevoxxFR 31 $ /usr/jdk/jdk-9/bin/javac -sourcepath src src/Hello.java $ /usr/jdk/jdk-9/bin/javap -c

    -private src/Hello.class private static java.lang.String hello(java.lang.Object, int) 0: aload_0 1: iload_1 2: invokedynamic #2, 0 (Ljava/lang/Object;I)Ljava/lang/String; // java.lang.invoke.StringConcatFactory.makeConcatWithConstants: (...Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; // BSM arguments: hello\0\0 7: areturn Javac & javap on 1.9 Construction pattern
  32. #DevoxxFR 32 Different strategies available classical StringBuilder (default) estimate size

    + StringBuilder exact size + StringBuilder estimate size + StringBulder using MethodHandle exact size + StringBuilder using MethodHandle exact size + byte[] using MethodHandle
  33. #DevoxxFR 33 if (cl == Integer.TYPE) { return 11; }

    // "-2147483648" if (cl == Boolean.TYPE) { return 5; } // "false" if (cl == Byte.TYPE) { return 4; } // "-128" if (cl == Character.TYPE) { return 1; } // duh if (cl == Short.TYPE) { return 6; } // "-32768" if (cl == Double.TYPE) { return 26; } // apparently, no larger than this if (cl == Float.TYPE) { return 26; } // see FloatingDecimal.BinaryToASCIIBuffer if (cl == Long.TYPE) { return 20; } // "-9223372036854775808" Estimate the size of a primitive value
  34. #DevoxxFR # {method} {0x00007ff4ddc002f0} 'hello' '(Ljava/lang/Object;I)Ljava/lang/String;' in 'Hello' # parm0:

    rsi:rsi = 'java/lang/Object' # parm1: rdx = int # [sp+0x50] (sp of caller) 0x00007ff513491240: mov %eax,-0x14000(%rsp) 0x00007ff513491247: push %rbp 0x00007ff513491248: sub $0x40,%rsp ;*synchronization entry ; - Hello::hello@-1 (line 3) 0x00007ff51349124c: mov %edx,(%rsp) 0x00007ff51349124f: mov 0x8(%rsi),%r10d ; implicit exception 0x00007ff513491253: cmp $0xf80002da,%r10d ; {metadata('java/lang/String')} 0x00007ff51349125a: jne 0x00007ff513491539 ;*invokevirtual toString 0x00007ff513491260: mov %rsi,0x8(%rsp) 0x00007ff513491265: test %edx,%edx 0x00007ff513491267: jl 0x00007ff513491581 ;* java.lang.Integer::stringSize@3 0x00007ff51349126d: mov %edx,%ebp 0x00007ff51349126f: neg %ebp 0x00007ff513491271: cmp $0xfffffffffffffff6,%ebp 0x00007ff513491274: jle 0x00007ff51349127e 0x00007ff513491276: mov $0x1,%r11d 0x00007ff51349127c: jmp 0x00007ff5134912da 0x00007ff51349127e: mov $0x2,%r11d 0x00007ff513491284: cmp $0xffffffffffffff9c,%ebp 0x00007ff513491287: jg 0x00007ff5134912da 0x00007ff513491289: cmp $0xfffffc18,%ebp 0x00007ff51349128f: jg 0x00007ff5134912d7 0x00007ff513491291: mov $0x4,%r11d 0x00007ff513491297: cmp $0xffffd8f0,%ebp 0x00007ff51349129d: jg 0x00007ff5134912da 0x00007ff51349129f: cmp $0xfffe7960,%ebp 0x00007ff5134912a5: jg 0x00007ff5134912d7 0x00007ff5134912a7: mov $0x6,%r11d 0x00007ff5134912ad: cmp $0xfff0bdc0,%ebp 0x00007ff5134912b3: jg 0x00007ff5134912da 0x00007ff5134912b5: cmp $0xff676980,%ebp 0x00007ff5134912bb: jg 0x00007ff5134912d7 0x00007ff5134912bd: mov $0x8,%r11d 0x00007ff5134912c3: cmp $0xfa0a1f00,%ebp 0x00007ff5134912c9: jg 0x00007ff5134912da 0x00007ff5134912cb: cmp $0xc4653600,%ebp 0x00007ff5134912d1: jle 0x00007ff5134915fd 0x00007ff5134912d7: inc %r11d ;*iinc 0x00007ff5134912da: mov 0xc(%rsi),%r10d ;*getfield value 0x00007ff5134912de: mov 0xc(%r12,%r10,8),%ebp ; implicit exception 0x00007ff5134912e3: movsbl 0x14(%rsi),%r8d ;*getfield coder 0x00007ff5134912e8: mov %r8d,%ecx 0x00007ff5134912eb: sar %cl,%ebp 0x00007ff5134912ed: add %r11d,%ebp 0x00007ff5134912f0: add $0x5,%ebp ;*iadd 0x00007ff5134912f3: test %ebp,%ebp 0x00007ff5134912f5: jl 0x00007ff5134915c5 ; j.l.StringConcatHelper::checkOverflow 0x00007ff5134912fb: mov %ebp,%r10d 0x00007ff5134912fe: shl %cl,%r10d 0x00007ff513491301: mov %r10d,0x4(%rsp) 0x00007ff513491306: test %r10d,%r10d 0x00007ff513491309: jl 0x00007ff5134915d9 ; jdk.internal.misc.Unsafe::allocateUninitializedArray • 34 What does the JIT generate ? 0x00007ff51349130f: cmp $0x100000,%r10d 0x00007ff513491316: ja 0x00007ff513491531 0x00007ff51349131c: movslq %r10d,%r10 0x00007ff51349131f: mov 0x4(%rsp),%r11d 0x00007ff513491324: cmp $0x100000,%r11d 0x00007ff51349132b: ja 0x00007ff5134914f4 0x00007ff513491331: mov 0x60(%r15),%rax 0x00007ff513491335: add $0x17,%r10 0x00007ff513491339: and $0xfffffffffffffff8,%r10 0x00007ff51349133d: mov %rax,%r11 0x00007ff513491340: add %r10,%r11 0x00007ff513491343: cmp 0x70(%r15),%r11 0x00007ff513491347: jae 0x00007ff5134914f4 0x00007ff51349134d: mov %r11,0x60(%r15) 0x00007ff513491351: prefetchw 0xc0(%r11) 0x00007ff513491359: movq $0x1,(%rax) 0x00007ff513491360: prefetchw 0x100(%r11) 0x00007ff513491368: movl $0xf80000f5,0x8(%rax) ; {metadata({type array byte})} 0x00007ff51349136f: mov 0x4(%rsp),%r9d 0x00007ff513491374: mov %r9d,0xc(%rax) 0x00007ff513491378: prefetchw 0x140(%r11) 0x00007ff513491380: prefetchw 0x180(%r11) 0x00007ff513491388: mov %rax,0x10(%rsp) 0x00007ff51349138d: test %r8d,%r8d 0x00007ff513491390: jne 0x00007ff513491595 0x00007ff513491396: mov (%rsp),%esi 0x00007ff513491399: mov %ebp,%edx 0x00007ff51349139b: mov %rax,%rcx 0x00007ff51349139e: mov %r8d,%ebp 0x00007ff5134913a1: xchg %ax,%ax 0x00007ff5134913a3: callq 0x00007ff51348d800 ;*invokestatic getChars 0x00007ff5134913a8: mov 0x8(%rsp),%r10 0x00007ff5134913ad: mov 0xc(%r10),%r10d ;*getfield value 0x00007ff5134913b1: mov 0xc(%r12,%r10,8),%ebp ;*arraylength 0x00007ff5134913b6: mov 0x8(%rsp),%r11 0x00007ff5134913bb: movsbl 0x14(%r11),%ecx ;*getfield coder 0x00007ff5134913c0: mov %ebp,%r11d 0x00007ff5134913c3: sar %cl,%r11d 0x00007ff5134913c6: mov %eax,%ebx 0x00007ff5134913c8: sub %r11d,%ebx 0x00007ff5134913cb: test %ecx,%ecx 0x00007ff5134913cd: jne 0x00007ff5134915a9 0x00007ff5134913d3: test %ebx,%ebx 0x00007ff5134913d5: jl 0x00007ff513491551 0x00007ff5134913db: mov %ebx,%r8d 0x00007ff5134913de: add %ebp,%r8d 0x00007ff5134913e1: mov 0x4(%rsp),%r13d 0x00007ff5134913e6: cmp %r8d,%r13d 0x00007ff5134913e9: jb 0x00007ff513491551 ;*invokestatic arraycopy 0x00007ff5134913ef: test %ebp,%ebp 0x00007ff5134913f1: jbe 0x00007ff513491416 0x00007ff5134913f3: mov 0x10(%rsp),%r11 0x00007ff5134913f8: lea 0x10(%r11,%rbx,1),%rsi 0x00007ff5134913fd: movslq %ebp,%rdx 0x00007ff513491400: lea (%r12,%r10,8),%r11 ;*getfield value 0x00007ff513491404: lea 0x10(%r12,%r10,8),%rdi 0x00007ff513491409: mov $0x7ff51343baa0,%r10 0x00007ff513491413: callq *%r10 ;*invokestatic arraycopy • 0x00007ff513491416: mov %ebx,%ebp 0x00007ff513491418: add $0xfffffffffffffffb,%ebp 0x00007ff51349141b: test %ebp,%ebp 0x00007ff51349141d: jl 0x00007ff513491569 0x00007ff513491423: cmp %ebx,%r13d 0x00007ff513491426: jb 0x00007ff513491569 0x00007ff51349142c: movslq %ebp,%r10 0x00007ff51349142f: test %ebp,%ebp 0x00007ff513491431: jg 0x00007ff5134914cc 0x00007ff513491437: mov 0x10(%rsp),%r11 0x00007ff51349143c: movb $0x68,0x10(%r11,%r10,1) 0x00007ff513491442: movb $0x65,0x11(%r11,%r10,1) 0x00007ff513491448: movb $0x6c,0x12(%r11,%r10,1) 0x00007ff51349144e: movb $0x6c,0x13(%r11,%r10,1) 0x00007ff513491454: movb $0x6f,0x14(%r11,%r10,1) ;*invokestatic arraycopy 0x00007ff51349145a: test %ebp,%ebp 0x00007ff51349145c: jne 0x00007ff5134915ed ; InlineCopyStrategy::checkIndex@1 0x00007ff513491462: mov 0x60(%r15),%rax 0x00007ff513491466: mov %rax,%r10 0x00007ff513491469: add $0x18,%r10 0x00007ff51349146d: cmp 0x70(%r15),%r10 0x00007ff513491471: jae 0x00007ff513491516 0x00007ff513491477: mov %r10,0x60(%r15) 0x00007ff51349147b: prefetchw 0xc0(%r10) 0x00007ff513491483: mov $0xf80002da,%r11d ; {metadata('java/lang/String')} 0x00007ff513491489: mov $0x0,%r10 0x00007ff513491493: lea (%r10,%r11,8),%r10 0x00007ff513491497: mov 0xb0(%r10),%r10 0x00007ff51349149e: mov %r10,(%rax) 0x00007ff5134914a1: movl $0xf80002da,0x8(%rax) ; {metadata('java/lang/String')} 0x00007ff5134914a8: mov %r12d,0xc(%rax) 0x00007ff5134914ac: mov %r12,0x10(%rax) 0x00007ff5134914b0: mov 0x10(%rsp),%r10 0x00007ff5134914b5: mov %r10,%r11 0x00007ff5134914b8: shr $0x3,%r11 0x00007ff5134914bc: mov %r11d,0xc(%rax) 0x00007ff5134914c0: add $0x40,%rsp 0x00007ff5134914c4: pop %rbp 0x00007ff5134914c5: test %eax,0x13321b35(%rip) 0x00007ff5134914cb: retq • Java 9, exact size + byte[] with method handle
  35. #DevoxxFR 35 Why the assembly code is a little bigger

    ?
  36. #DevoxxFR 36 The String representation has changed (again) in 9

    public final class String implements ... { @Stable private final byte[] value; // contains 8bits LATIN1 or 16bits UTF16 private final byte coder; // LATIN1 | UTF16 … } Compact String – JEP 254
  37. #DevoxxFR 37 Introduced by JEP 280 use invokedynamic to handle

    StringConcat at JDK level (not at JIT Level) Performance: 10% to 4x but need to recompile :( Indify String Concat
  38. #DevoxxFR 38 We need you ! Compact Strings + Indify

    String Concat has a lot of potential regressions ! Please, test the JDK9 EA build