Gdy już wiemy, czym są funkcje wyższego rzędu w Kotlinie, to możemy zadać pytanie – z wydajnością?
Cóż… JVM ma swoje ograniczenia i był projektowany z myślą o programowaniu obiektowym. Jednak natura funkcyjna powoduje, że mamy dużą ilość obiektów pośredniczących. Każda funkcja to osobny obiekt skojarzony z klasą, która jest reprezentowana jako inny obiekt. Coś, co ma sens w przypadku programowania obiektowego, gdzie poszczególne warstwy dostarczają części funkcjonalności tu, kończy się „nadprodukcją” stosu (dołóżmy jeszcze do tego dużo konstrukcji invoke virtual).
Twórcy języka Kotlin mając świadomość tego problemu postanowili wbudować w język mechanizm zwany inliningiem.
Problem
Przyjrzyjmy się poniższemu programowi:
Listing 1. „Klasyczne” wykorzystanie wyrażenia lambda jako HOF
fun main(args: Array<String>) { timingLog(1, 2, { a, b -> a + b }) } fun <T, U, R> timingLog(a: T, b: U, f: (T, U) -> R): R { val watch = Stopwatch.createStarted() try { return f(a, b) }finally{ println("call in ns: " + watch.elapsed(TimeUnit.NANOSECONDS)) } }
Mamy tu funkcję, która jako parametr przyjmuje inną funkcję i jej wywołanie obudowuje logowaniem czasu wykonania (PoC, bo tego się tak nie robi). Cała „magia” dzieje się w funkcji timingLog. Wprowadźmy słowo kluczowe inline:
Listing 2. Wywołanie funkcji inline
fun main(args: Array<String>) { timingLog(1, 2, { a, b -> a + b }) } inline fun <T, U, R> timingLog(a: T, b: U, f: (T, U) -> R): R { val watch = Stopwatch.createStarted() try { return f(a, b) }finally{ println("call in ns: " + watch.elapsed(TimeUnit.NANOSECONDS)) } }
Z punktu widzenia kodu nic tu się nie zmieniło. Ot dodaliśmy słówko, które tylko „zaśmieca” kod. W rzeczywistości inline powoduje, że kompilator przenosi kod funkcji timingLog oraz przekazanej jej lambdy w miejsce wywołania tej pierwszej.
Prawda kodu, a prawda bytecodu
Co oznacza „przenosi”? Rzućmy okiem na bytecode wygenerowany dla pierwszego przypadku:
Listing 3. bytecode pierwszego przypadku
Classfile /home/koziolek/workspace/oldone/blog_old/target/classes/pl/koziolekweb/blog/inlines/InlinesKt.class Last modified 2016-05-23; size 2658 bytes MD5 checksum d770a2c85d4400192ea83e0c72e4bd84 Compiled from "Inlines.kt" public final class pl.koziolekweb.blog.inlines.InlinesKt minor version: 0 major version: 50 flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER Constant pool: #1 = Utf8 pl/koziolekweb/blog/inlines/InlinesKt #2 = Class #1 // pl/koziolekweb/blog/inlines/InlinesKt #3 = Utf8 java/lang/Object #4 = Class #3 // java/lang/Object #5 = Utf8 main #6 = Utf8 ([Ljava/lang/String;)V #7 = Utf8 Lorg/jetbrains/annotations/NotNull; #8 = Utf8 args #9 = String #8 // args #10 = Utf8 kotlin/jvm/internal/Intrinsics #11 = Class #10 // kotlin/jvm/internal/Intrinsics #12 = Utf8 checkParameterIsNotNull #13 = Utf8 (Ljava/lang/Object;Ljava/lang/String;)V #14 = NameAndType #12:#13 // checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V #15 = Methodref #11.#14 // kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V #16 = Utf8 java/lang/Integer #17 = Class #16 // java/lang/Integer #18 = Utf8 valueOf #19 = Utf8 (I)Ljava/lang/Integer; #20 = NameAndType #18:#19 // valueOf:(I)Ljava/lang/Integer; #21 = Methodref #17.#20 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #22 = Utf8 pl/koziolekweb/blog/inlines/InlinesKt$main$1 #23 = Class #22 // pl/koziolekweb/blog/inlines/InlinesKt$main$1 #24 = Utf8 INSTANCE #25 = Utf8 Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #26 = NameAndType #24:#25 // INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #27 = Fieldref #23.#26 // pl/koziolekweb/blog/inlines/InlinesKt$main$1.INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #28 = Utf8 kotlin/jvm/functions/Function2 #29 = Class #28 // kotlin/jvm/functions/Function2 #30 = Utf8 timingLog #31 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; #32 = NameAndType #30:#31 // timingLog:(Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; #33 = Methodref #2.#32 // pl/koziolekweb/blog/inlines/InlinesKt.timingLog:(Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; #34 = Utf8 [Ljava/lang/String; #35 = Utf8 f #36 = String #35 // f #37 = Utf8 com/google/common/base/Stopwatch #38 = Class #37 // com/google/common/base/Stopwatch #39 = Utf8 createStarted #40 = Utf8 ()Lcom/google/common/base/Stopwatch; #41 = NameAndType #39:#40 // createStarted:()Lcom/google/common/base/Stopwatch; #42 = Methodref #38.#41 // com/google/common/base/Stopwatch.createStarted:()Lcom/google/common/base/Stopwatch; #43 = Utf8 invoke #44 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #45 = NameAndType #43:#44 // invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #46 = InterfaceMethodref #29.#45 // kotlin/jvm/functions/Function2.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #47 = Utf8 java/lang/StringBuilder #48 = Class #47 // java/lang/StringBuilder #49 = Utf8 <init> #50 = Utf8 ()V #51 = NameAndType #49:#50 // "<init>":()V #52 = Methodref #48.#51 // java/lang/StringBuilder."<init>":()V #53 = Utf8 call in ns: #54 = String #53 // call in ns: #55 = Utf8 append #56 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #57 = NameAndType #55:#56 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #58 = Methodref #48.#57 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #59 = Utf8 java/util/concurrent/TimeUnit #60 = Class #59 // java/util/concurrent/TimeUnit #61 = Utf8 NANOSECONDS #62 = Utf8 Ljava/util/concurrent/TimeUnit; #63 = NameAndType #61:#62 // NANOSECONDS:Ljava/util/concurrent/TimeUnit; #64 = Fieldref #60.#63 // java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; #65 = Utf8 elapsed #66 = Utf8 (Ljava/util/concurrent/TimeUnit;)J #67 = NameAndType #65:#66 // elapsed:(Ljava/util/concurrent/TimeUnit;)J #68 = Methodref #38.#67 // com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J #69 = Utf8 (J)Ljava/lang/StringBuilder; #70 = NameAndType #55:#69 // append:(J)Ljava/lang/StringBuilder; #71 = Methodref #48.#70 // java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; #72 = Utf8 toString #73 = Utf8 ()Ljava/lang/String; #74 = NameAndType #72:#73 // toString:()Ljava/lang/String; #75 = Methodref #48.#74 // java/lang/StringBuilder.toString:()Ljava/lang/String; #76 = Utf8 java/lang/System #77 = Class #76 // java/lang/System #78 = Utf8 out #79 = Utf8 Ljava/io/PrintStream; #80 = NameAndType #78:#79 // out:Ljava/io/PrintStream; #81 = Fieldref #77.#80 // java/lang/System.out:Ljava/io/PrintStream; #82 = Utf8 java/io/PrintStream #83 = Class #82 // java/io/PrintStream #84 = Utf8 println #85 = Utf8 (Ljava/lang/Object;)V #86 = NameAndType #84:#85 // println:(Ljava/lang/Object;)V #87 = Methodref #83.#86 // java/io/PrintStream.println:(Ljava/lang/Object;)V #88 = Utf8 watch #89 = Utf8 Lcom/google/common/base/Stopwatch; #90 = Utf8 a #91 = Utf8 Ljava/lang/Object; #92 = Utf8 b #93 = Utf8 Lkotlin/jvm/functions/Function2; #94 = Utf8 java/lang/Throwable #95 = Class #94 // java/lang/Throwable #96 = Utf8 Lkotlin/Metadata; #97 = Utf8 mv #98 = Integer 1 #99 = Utf8 bv #100 = Integer 0 #101 = Utf8 k #102 = Integer 2 #103 = Utf8 d1 #104 = Utf8 \n \n\n \n\n\n\n\n 0200¢GH\" \"\t\"2\nH2H\t2HH\tH0\r¢¨ #105 = Utf8 d2 #106 = Utf8 #107 = Utf8 R #108 = Utf8 T #109 = Utf8 U #110 = Utf8 Lkotlin/Function2; #111 = Utf8 blog #112 = Utf8 Inlines.kt #113 = Utf8 Code #114 = Utf8 LocalVariableTable #115 = Utf8 LineNumberTable #116 = Utf8 RuntimeInvisibleParameterAnnotations #117 = Utf8 StackMapTable #118 = Utf8 Signature #119 = Utf8 <T:Ljava/lang/Object;U:Ljava/lang/Object;R:Ljava/lang/Object;>(TT;TU;Lkotlin/jvm/functions/Function2<-TT;-TU;+TR;>;)TR; #120 = Utf8 SourceFile #121 = Utf8 SourceDebugExtension #122 = Utf8 InnerClasses #123 = Utf8 RuntimeVisibleAnnotations { public static final void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL Code: stack=3, locals=1, args_size=1 0: aload_0 1: ldc #9 // String args 3: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V 6: iconst_1 7: invokestatic #21 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 10: iconst_2 11: invokestatic #21 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 14: getstatic #27 // Field pl/koziolekweb/blog/inlines/InlinesKt$main$1.INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; 17: checkcast #29 // class kotlin/jvm/functions/Function2 20: invokestatic #33 // Method timingLog:(Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; 23: pop 24: return LocalVariableTable: Start Length Slot Name Signature 0 25 0 args [Ljava/lang/String; LineNumberTable: line 14: 6 line 16: 24 RuntimeInvisibleParameterAnnotations: 0: 0: #7() public static final <T extends java.lang.Object, U extends java.lang.Object, R extends java.lang.Object> R timingLog(T, U, kotlin.jvm.functions.Function2<? super T, ? super U, ? extends R>); descriptor: (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL Code: stack=3, locals=6, args_size=3 0: aload_2 1: ldc #36 // String f 3: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V 6: invokestatic #42 // Method com/google/common/base/Stopwatch.createStarted:()Lcom/google/common/base/Stopwatch; 9: astore_3 10: nop 11: aload_2 12: aload_0 13: aload_1 14: invokeinterface #46, 3 // InterfaceMethod kotlin/jvm/functions/Function2.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; 19: astore 4 21: new #48 // class java/lang/StringBuilder 24: dup 25: invokespecial #52 // Method java/lang/StringBuilder."<init>":()V 28: ldc #54 // String call in ns: 30: invokevirtual #58 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 33: aload_3 34: getstatic #64 // Field java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; 37: invokevirtual #68 // Method com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J 40: invokevirtual #71 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 43: invokevirtual #75 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 46: astore 5 48: nop 49: getstatic #81 // Field java/lang/System.out:Ljava/io/PrintStream; 52: aload 5 54: invokevirtual #87 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 57: aload 4 59: areturn 60: astore 4 62: new #48 // class java/lang/StringBuilder 65: dup 66: invokespecial #52 // Method java/lang/StringBuilder."<init>":()V 69: ldc #54 // String call in ns: 71: invokevirtual #58 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 74: aload_3 75: getstatic #64 // Field java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; 78: invokevirtual #68 // Method com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J 81: invokevirtual #71 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 84: invokevirtual #75 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 87: astore 5 89: nop 90: getstatic #81 // Field java/lang/System.out:Ljava/io/PrintStream; 93: aload 5 95: invokevirtual #87 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 98: aload 4 100: athrow Exception table: from to target type 10 21 60 any 60 62 60 any LocalVariableTable: Start Length Slot Name Signature 10 91 3 watch Lcom/google/common/base/Stopwatch; 0 101 0 a Ljava/lang/Object; 0 101 1 b Ljava/lang/Object; 0 101 2 f Lkotlin/jvm/functions/Function2; LineNumberTable: line 19: 6 line 20: 10 line 21: 11 line 23: 21 line 23: 60 line 23: 62 StackMapTable: number_of_entries = 1 frame_type = 255 /* full_frame */ offset_delta = 60 locals = [ class java/lang/Object, class java/lang/Object, class kotlin/jvm/functions/Function2, class com/google/common/base/Stopwatch ] stack = [ class java/lang/Throwable ] Signature: #119 // <T:Ljava/lang/Object;U:Ljava/lang/Object;R:Ljava/lang/Object;>(TT;TU;Lkotlin/jvm/functions/Function2<-TT;-TU;+TR;>;)TR; RuntimeInvisibleParameterAnnotations: 0: 1: 2: 0: #7() } SourceFile: "Inlines.kt" SourceDebugExtension: SMAP Inlines.kt Kotlin *S Kotlin *F + 1 Inlines.kt pl/koziolekweb/blog/inlines/InlinesKt *L 1#1,25:1 *E InnerClasses: static final #23; //class pl/koziolekweb/blog/inlines/InlinesKt$main$1 RuntimeVisibleAnnotations: 0: #96(#97=[I#98,I#98,I#98],#99=[I#98,I#100,I#100],#101=I#102,#103=[s#104],#105=[s#5,s#106,s#8,s#106,s#106,s#6,s#30,s#107,s#108,s#109,s#90,s#92,s#35,s#110,s#31,s#111])
Oraz to samo dla drugiego przypadku:
Listing 4. Wpływ inline na bytecode
Classfile /home/koziolek/workspace/oldone/blog_old/target/classes/pl/koziolekweb/blog/inlines/InlinesKt.class Last modified 2016-05-23; size 2890 bytes MD5 checksum 75e883aed086b2f08d1b37244b23de8c Compiled from "Inlines.kt" public final class pl.koziolekweb.blog.inlines.InlinesKt minor version: 0 major version: 50 flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER Constant pool: #1 = Utf8 pl/koziolekweb/blog/inlines/InlinesKt #2 = Class #1 // pl/koziolekweb/blog/inlines/InlinesKt #3 = Utf8 java/lang/Object #4 = Class #3 // java/lang/Object #5 = Utf8 main #6 = Utf8 ([Ljava/lang/String;)V #7 = Utf8 Lorg/jetbrains/annotations/NotNull; #8 = Utf8 args #9 = String #8 // args #10 = Utf8 kotlin/jvm/internal/Intrinsics #11 = Class #10 // kotlin/jvm/internal/Intrinsics #12 = Utf8 checkParameterIsNotNull #13 = Utf8 (Ljava/lang/Object;Ljava/lang/String;)V #14 = NameAndType #12:#13 // checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V #15 = Methodref #11.#14 // kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V #16 = Utf8 com/google/common/base/Stopwatch #17 = Class #16 // com/google/common/base/Stopwatch #18 = Utf8 createStarted #19 = Utf8 ()Lcom/google/common/base/Stopwatch; #20 = NameAndType #18:#19 // createStarted:()Lcom/google/common/base/Stopwatch; #21 = Methodref #17.#20 // com/google/common/base/Stopwatch.createStarted:()Lcom/google/common/base/Stopwatch; #22 = Utf8 java/lang/StringBuilder #23 = Class #22 // java/lang/StringBuilder #24 = Utf8 <init> #25 = Utf8 ()V #26 = NameAndType #24:#25 // "<init>":()V #27 = Methodref #23.#26 // java/lang/StringBuilder."<init>":()V #28 = Utf8 call in ns: #29 = String #28 // call in ns: #30 = Utf8 append #31 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #32 = NameAndType #30:#31 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #33 = Methodref #23.#32 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #34 = Utf8 java/util/concurrent/TimeUnit #35 = Class #34 // java/util/concurrent/TimeUnit #36 = Utf8 NANOSECONDS #37 = Utf8 Ljava/util/concurrent/TimeUnit; #38 = NameAndType #36:#37 // NANOSECONDS:Ljava/util/concurrent/TimeUnit; #39 = Fieldref #35.#38 // java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; #40 = Utf8 elapsed #41 = Utf8 (Ljava/util/concurrent/TimeUnit;)J #42 = NameAndType #40:#41 // elapsed:(Ljava/util/concurrent/TimeUnit;)J #43 = Methodref #17.#42 // com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J #44 = Utf8 (J)Ljava/lang/StringBuilder; #45 = NameAndType #30:#44 // append:(J)Ljava/lang/StringBuilder; #46 = Methodref #23.#45 // java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; #47 = Utf8 toString #48 = Utf8 ()Ljava/lang/String; #49 = NameAndType #47:#48 // toString:()Ljava/lang/String; #50 = Methodref #23.#49 // java/lang/StringBuilder.toString:()Ljava/lang/String; #51 = Utf8 java/lang/System #52 = Class #51 // java/lang/System #53 = Utf8 out #54 = Utf8 Ljava/io/PrintStream; #55 = NameAndType #53:#54 // out:Ljava/io/PrintStream; #56 = Fieldref #52.#55 // java/lang/System.out:Ljava/io/PrintStream; #57 = Utf8 java/io/PrintStream #58 = Class #57 // java/io/PrintStream #59 = Utf8 println #60 = Utf8 (Ljava/lang/Object;)V #61 = NameAndType #59:#60 // println:(Ljava/lang/Object;)V #62 = Methodref #58.#61 // java/io/PrintStream.println:(Ljava/lang/Object;)V #63 = Utf8 a #64 = Utf8 I #65 = Utf8 b #66 = Utf8 $i$a$1$timingLog #67 = Utf8 watch$iv #68 = Utf8 Lcom/google/common/base/Stopwatch; #69 = Utf8 a$iv #70 = Utf8 b$iv #71 = Utf8 $i$f$timingLog #72 = Utf8 [Ljava/lang/String; #73 = Class #72 // "[Ljava/lang/String;" #74 = Utf8 java/lang/Throwable #75 = Class #74 // java/lang/Throwable #76 = Utf8 java/lang/String #77 = Class #76 // java/lang/String #78 = Utf8 timingLog #79 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; #80 = Utf8 f #81 = String #80 // f #82 = Utf8 kotlin/jvm/functions/Function2 #83 = Class #82 // kotlin/jvm/functions/Function2 #84 = Utf8 invoke #85 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #86 = NameAndType #84:#85 // invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #87 = InterfaceMethodref #83.#86 // kotlin/jvm/functions/Function2.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #88 = Utf8 kotlin/jvm/internal/InlineMarker #89 = Class #88 // kotlin/jvm/internal/InlineMarker #90 = Utf8 finallyStart #91 = Utf8 (I)V #92 = NameAndType #90:#91 // finallyStart:(I)V #93 = Methodref #89.#92 // kotlin/jvm/internal/InlineMarker.finallyStart:(I)V #94 = Utf8 finallyEnd #95 = NameAndType #94:#91 // finallyEnd:(I)V #96 = Methodref #89.#95 // kotlin/jvm/internal/InlineMarker.finallyEnd:(I)V #97 = Utf8 watch #98 = Utf8 Ljava/lang/Object; #99 = Utf8 Lkotlin/jvm/functions/Function2; #100 = Utf8 Lkotlin/Metadata; #101 = Utf8 mv #102 = Integer 1 #103 = Utf8 bv #104 = Integer 0 #105 = Utf8 k #106 = Integer 2 #107 = Utf8 d1 #108 = Utf8 \n \n\n \n\n\n\n\n 0200¢JH\" \"\t\"2\nH2H\t2HH\tH0\rH¢¨ #109 = Utf8 d2 #110 = Utf8 #111 = Utf8 R #112 = Utf8 T #113 = Utf8 U #114 = Utf8 Lkotlin/Function2; #115 = Utf8 blog #116 = Utf8 Inlines.kt #117 = Utf8 Code #118 = Utf8 LocalVariableTable #119 = Utf8 LineNumberTable #120 = Utf8 StackMapTable #121 = Utf8 RuntimeInvisibleParameterAnnotations #122 = Utf8 Signature #123 = Utf8 <T:Ljava/lang/Object;U:Ljava/lang/Object;R:Ljava/lang/Object;>(TT;TU;Lkotlin/jvm/functions/Function2<-TT;-TU;+TR;>;)TR; #124 = Utf8 SourceFile #125 = Utf8 SourceDebugExtension #126 = Utf8 RuntimeVisibleAnnotations { public static final void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL Code: stack=3, locals=8, args_size=1 0: aload_0 1: ldc #9 // String args 3: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V 6: iconst_1 7: istore_1 8: iconst_2 9: istore_2 10: nop 11: invokestatic #21 // Method com/google/common/base/Stopwatch.createStarted:()Lcom/google/common/base/Stopwatch; 14: astore_3 15: nop 16: iload_1 17: iload_2 18: istore 4 20: istore 5 22: iload 5 24: iload 4 26: iadd 27: istore 5 29: new #23 // class java/lang/StringBuilder 32: dup 33: invokespecial #27 // Method java/lang/StringBuilder."<init>":()V 36: ldc #29 // String call in ns: 38: invokevirtual #33 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 41: aload_3 42: getstatic #39 // Field java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; 45: invokevirtual #43 // Method com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J 48: invokevirtual #46 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 51: invokevirtual #50 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 54: astore 4 56: nop 57: getstatic #56 // Field java/lang/System.out:Ljava/io/PrintStream; 60: aload 4 62: invokevirtual #62 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 65: iload 5 67: goto 111 70: astore 5 72: new #23 // class java/lang/StringBuilder 75: dup 76: invokespecial #27 // Method java/lang/StringBuilder."<init>":()V 79: ldc #29 // String call in ns: 81: invokevirtual #33 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 84: aload_3 85: getstatic #39 // Field java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; 88: invokevirtual #43 // Method com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J 91: invokevirtual #46 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 94: invokevirtual #50 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 97: astore 4 99: nop 100: getstatic #56 // Field java/lang/System.out:Ljava/io/PrintStream; 103: aload 4 105: invokevirtual #62 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 108: aload 5 110: athrow 111: pop 112: return Exception table: from to target type 15 29 70 any 70 72 70 any LocalVariableTable: Start Length Slot Name Signature 22 5 5 a I 22 5 4 b I 22 5 6 $i$a$1$timingLog I 15 96 3 watch$iv Lcom/google/common/base/Stopwatch; 11 100 1 a$iv I 11 100 2 b$iv I 11 100 7 $i$f$timingLog I 0 113 0 args [Ljava/lang/String; LineNumberTable: line 14: 6 line 19: 11 line 20: 15 line 21: 16 line 14: 22 line 23: 29 line 23: 72 line 16: 112 StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 70 locals = [ class "[Ljava/lang/String;", int, int, class com/google/common/base/Stopwatch ] stack = [ class java/lang/Throwable ] frame_type = 255 /* full_frame */ offset_delta = 40 locals = [ class "[Ljava/lang/String;", int, int, class com/google/common/base/Stopwatch, class java/lang/String, int ] stack = [ int ] RuntimeInvisibleParameterAnnotations: 0: 0: #7() public static final <T extends java.lang.Object, U extends java.lang.Object, R extends java.lang.Object> R timingLog(T, U, kotlin.jvm.functions.Function2<? super T, ? super U, ? extends R>); descriptor: (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL Code: stack=3, locals=7, args_size=3 0: aload_2 1: ldc #81 // String f 3: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V 6: invokestatic #21 // Method com/google/common/base/Stopwatch.createStarted:()Lcom/google/common/base/Stopwatch; 9: astore 4 11: nop 12: aload_2 13: aload_0 14: aload_1 15: invokeinterface #87, 3 // InterfaceMethod kotlin/jvm/functions/Function2.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; 20: astore 5 22: iconst_1 23: invokestatic #93 // Method kotlin/jvm/internal/InlineMarker.finallyStart:(I)V 26: new #23 // class java/lang/StringBuilder 29: dup 30: invokespecial #27 // Method java/lang/StringBuilder."<init>":()V 33: ldc #29 // String call in ns: 35: invokevirtual #33 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 38: aload 4 40: getstatic #39 // Field java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; 43: invokevirtual #43 // Method com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J 46: invokevirtual #46 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 49: invokevirtual #50 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 52: astore 6 54: nop 55: getstatic #56 // Field java/lang/System.out:Ljava/io/PrintStream; 58: aload 6 60: invokevirtual #62 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 63: iconst_1 64: invokestatic #96 // Method kotlin/jvm/internal/InlineMarker.finallyEnd:(I)V 67: aload 5 69: areturn 70: astore 5 72: iconst_1 73: invokestatic #93 // Method kotlin/jvm/internal/InlineMarker.finallyStart:(I)V 76: new #23 // class java/lang/StringBuilder 79: dup 80: invokespecial #27 // Method java/lang/StringBuilder."<init>":()V 83: ldc #29 // String call in ns: 85: invokevirtual #33 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 88: aload 4 90: getstatic #39 // Field java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; 93: invokevirtual #43 // Method com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J 96: invokevirtual #46 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 99: invokevirtual #50 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 102: astore 6 104: nop 105: getstatic #56 // Field java/lang/System.out:Ljava/io/PrintStream; 108: aload 6 110: invokevirtual #62 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 113: iconst_1 114: invokestatic #96 // Method kotlin/jvm/internal/InlineMarker.finallyEnd:(I)V 117: aload 5 119: athrow Exception table: from to target type 11 22 70 any 70 72 70 any LocalVariableTable: Start Length Slot Name Signature 11 109 4 watch Lcom/google/common/base/Stopwatch; 0 120 0 a Ljava/lang/Object; 0 120 1 b Ljava/lang/Object; 0 120 2 f Lkotlin/jvm/functions/Function2; 0 120 3 $i$f$timingLog I LineNumberTable: line 19: 6 line 20: 11 line 21: 12 line 23: 26 line 23: 70 line 23: 76 StackMapTable: number_of_entries = 1 frame_type = 255 /* full_frame */ offset_delta = 70 locals = [ class java/lang/Object, class java/lang/Object, class kotlin/jvm/functions/Function2, top, class com/google/common/base/Stopwatch ] stack = [ class java/lang/Throwable ] Signature: #123 // <T:Ljava/lang/Object;U:Ljava/lang/Object;R:Ljava/lang/Object;>(TT;TU;Lkotlin/jvm/functions/Function2<-TT;-TU;+TR;>;)TR; RuntimeInvisibleParameterAnnotations: 0: 1: 2: 0: #7() } SourceFile: "Inlines.kt" SourceDebugExtension: SMAP Inlines.kt Kotlin *S Kotlin *F + 1 Inlines.kt pl/koziolekweb/blog/inlines/InlinesKt *L 1#1,26:1 *E RuntimeVisibleAnnotations: 0: #100(#101=[I#102,I#102,I#102],#103=[I#102,I#104,I#104],#105=I#106,#107=[s#108],#109=[s#5,s#110,s#8,s#110,s#110,s#6,s#78,s#111,s#112,s#113,s#63,s#65,s#80,s#114,s#79,s#115])
To, co nas tu najbardziej interesuje to definicja metody main. Na listingu 3 zaczyna się w linii 134, a na listingu 4 w linii 137. Jak widać w przypadku zastosowania inline metoda main „spuchła” i to znacznie. Po dokładniejszej analizie okaże się, że trafiły do niej wywołania z funkcji timingLog. Zatem proces związany z tym słowem kluczowym polega na „przepisaniu” kodu wywoływanego w miejsce gdzie jest on wywoływany… kojarzycie makra preprocesora z C? W pewien uproszczony sposób można o tym tak myśleć.
Pewnym bonusem jest brak dodatkowego pliku, który pojawia się, gdy nie używamy inline:
Listing 5. bytecode klasy wewnętrznej main
Classfile /home/koziolek/workspace/oldone/blog_old/target/classes/pl/koziolekweb/blog/inlines/InlinesKt$main$1.class Last modified 2016-05-23; size 1280 bytes MD5 checksum ab966e9707066ff871fe25438fd2debb Compiled from "Inlines.kt" final class pl.koziolekweb.blog.inlines.InlinesKt$main$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function2<java.lang.Integer, java.lang.Integer, java.lang.Integer> minor version: 0 major version: 50 flags: ACC_FINAL, ACC_SUPER Constant pool: #1 = Utf8 pl/koziolekweb/blog/inlines/InlinesKt$main$1 #2 = Class #1 // pl/koziolekweb/blog/inlines/InlinesKt$main$1 #3 = Utf8 Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function2<Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;>; #4 = Utf8 kotlin/jvm/internal/Lambda #5 = Class #4 // kotlin/jvm/internal/Lambda #6 = Utf8 kotlin/jvm/functions/Function2 #7 = Class #6 // kotlin/jvm/functions/Function2 #8 = Utf8 invoke #9 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #10 = Utf8 java/lang/Number #11 = Class #10 // java/lang/Number #12 = Utf8 intValue #13 = Utf8 ()I #14 = NameAndType #12:#13 // intValue:()I #15 = Methodref #11.#14 // java/lang/Number.intValue:()I #16 = Utf8 (II)I #17 = NameAndType #8:#16 // invoke:(II)I #18 = Methodref #2.#17 // pl/koziolekweb/blog/inlines/InlinesKt$main$1.invoke:(II)I #19 = Utf8 java/lang/Integer #20 = Class #19 // java/lang/Integer #21 = Utf8 valueOf #22 = Utf8 (I)Ljava/lang/Integer; #23 = NameAndType #21:#22 // valueOf:(I)Ljava/lang/Integer; #24 = Methodref #20.#23 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #25 = Utf8 this #26 = Utf8 Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #27 = Utf8 a #28 = Utf8 I #29 = Utf8 b #30 = Utf8 <init> #31 = Utf8 ()V #32 = Utf8 (I)V #33 = NameAndType #30:#32 // "<init>":(I)V #34 = Methodref #5.#33 // kotlin/jvm/internal/Lambda."<init>":(I)V #35 = Utf8 INSTANCE #36 = Utf8 <clinit> #37 = Utf8 Lkotlin/Metadata; #38 = Utf8 mv #39 = Integer 1 #40 = Utf8 bv #41 = Integer 0 #42 = Utf8 k #43 = Integer 3 #44 = Utf8 d1 #45 = Utf8 \n\n \n\n 02020H\n¢ #46 = Utf8 d2 #47 = Utf8 <anonymous> #48 = Utf8 #49 = Utf8 pl/koziolekweb/blog/inlines/InlinesKt #50 = Class #49 // pl/koziolekweb/blog/inlines/InlinesKt #51 = Utf8 main #52 = Utf8 ([Ljava/lang/String;)V #53 = NameAndType #51:#52 // main:([Ljava/lang/String;)V #54 = NameAndType #30:#31 // "<init>":()V #55 = Methodref #2.#54 // pl/koziolekweb/blog/inlines/InlinesKt$main$1."<init>":()V #56 = NameAndType #35:#26 // INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #57 = Fieldref #2.#56 // pl/koziolekweb/blog/inlines/InlinesKt$main$1.INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #58 = Utf8 Inlines.kt #59 = Utf8 Code #60 = Utf8 LocalVariableTable #61 = Utf8 LineNumberTable #62 = Utf8 Signature #63 = Utf8 SourceFile #64 = Utf8 EnclosingMethod #65 = Utf8 InnerClasses #66 = Utf8 RuntimeVisibleAnnotations { public static final pl.koziolekweb.blog.inlines.InlinesKt$main$1 INSTANCE; descriptor: Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL public java.lang.Object invoke(java.lang.Object, java.lang.Object); descriptor: (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=3, locals=3, args_size=3 0: aload_0 1: aload_1 2: checkcast #11 // class java/lang/Number 5: invokevirtual #15 // Method java/lang/Number.intValue:()I 8: aload_2 9: checkcast #11 // class java/lang/Number 12: invokevirtual #15 // Method java/lang/Number.intValue:()I 15: invokevirtual #18 // Method invoke:(II)I 18: invokestatic #24 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 21: areturn public final int invoke(int, int); descriptor: (II)I flags: ACC_PUBLIC, ACC_FINAL Code: stack=2, locals=3, args_size=3 0: iload_1 1: iload_2 2: iadd 3: ireturn LocalVariableTable: Start Length Slot Name Signature 0 4 0 this Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; 0 4 1 a I 0 4 2 b I LineNumberTable: line 14: 0 pl.koziolekweb.blog.inlines.InlinesKt$main$1(); descriptor: ()V flags: Code: stack=2, locals=1, args_size=1 0: aload_0 1: iconst_2 2: invokespecial #34 // Method kotlin/jvm/internal/Lambda."<init>":(I)V 5: return static {}; descriptor: ()V flags: ACC_STATIC Code: stack=2, locals=0, args_size=0 0: new #2 // class pl/koziolekweb/blog/inlines/InlinesKt$main$1 3: dup 4: invokespecial #55 // Method "<init>":()V 7: putstatic #57 // Field INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; 10: return } Signature: #3 // Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function2<Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;>; SourceFile: "Inlines.kt" EnclosingMethod: #50.#53 // pl.koziolekweb.blog.inlines.InlinesKt.main InnerClasses: static final #2; //class pl/koziolekweb/blog/inlines/InlinesKt$main$1 RuntimeVisibleAnnotations: 0: #37(#38=[I#39,I#39,I#39],#40=[I#39,I#41,I#41],#42=I#43,#44=[s#45],#46=[s#47,s#48,s#27,s#29,s#8])
Problemy
Zastosowanie tego mechanizmu spowoduje, że kod „utyje”. I to znacznie. Należy zatem ostrożnie podchodzić do tematu. Co, jeżeli nie chcemy, by któraś z lambd została przepisana? W tym celu należy użyć słowa noinline:
Listing 6. Kod z noinline
fun main(args: Array<String>) { timingLog(1, 2, { a, b -> a + b }) } inline fun <T, U, R>> timingLog(a: T, b: U, noinline f: (T, U) -> R): R { val watch = Stopwatch.createStarted() try { return f(a, b) }finally{ println("call in ns: " + watch.elapsed(TimeUnit.NANOSECONDS)) } }
W efekcie otrzymamy:
Listing 7. Kod z wykorzystaniem noinline
Classfile /home/koziolek/workspace/oldone/blog_old/target/classes/pl/koziolekweb/blog/inlines/InlinesKt.class Last modified 2016-05-23; size 3106 bytes MD5 checksum 01984b401a79bd5e6653d4100a183017 Compiled from "Inlines.kt" public final class pl.koziolekweb.blog.inlines.InlinesKt minor version: 0 major version: 50 flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER Constant pool: #1 = Utf8 pl/koziolekweb/blog/inlines/InlinesKt #2 = Class #1 // pl/koziolekweb/blog/inlines/InlinesKt #3 = Utf8 java/lang/Object #4 = Class #3 // java/lang/Object #5 = Utf8 main #6 = Utf8 ([Ljava/lang/String;)V #7 = Utf8 Lorg/jetbrains/annotations/NotNull; #8 = Utf8 args #9 = String #8 // args #10 = Utf8 kotlin/jvm/internal/Intrinsics #11 = Class #10 // kotlin/jvm/internal/Intrinsics #12 = Utf8 checkParameterIsNotNull #13 = Utf8 (Ljava/lang/Object;Ljava/lang/String;)V #14 = NameAndType #12:#13 // checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V #15 = Methodref #11.#14 // kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V #16 = Utf8 java/lang/Integer #17 = Class #16 // java/lang/Integer #18 = Utf8 valueOf #19 = Utf8 (I)Ljava/lang/Integer; #20 = NameAndType #18:#19 // valueOf:(I)Ljava/lang/Integer; #21 = Methodref #17.#20 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #22 = Utf8 pl/koziolekweb/blog/inlines/InlinesKt$main$1 #23 = Class #22 // pl/koziolekweb/blog/inlines/InlinesKt$main$1 #24 = Utf8 INSTANCE #25 = Utf8 Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #26 = NameAndType #24:#25 // INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #27 = Fieldref #23.#26 // pl/koziolekweb/blog/inlines/InlinesKt$main$1.INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #28 = Utf8 kotlin/jvm/functions/Function2 #29 = Class #28 // kotlin/jvm/functions/Function2 #30 = Utf8 com/google/common/base/Stopwatch #31 = Class #30 // com/google/common/base/Stopwatch #32 = Utf8 createStarted #33 = Utf8 ()Lcom/google/common/base/Stopwatch; #34 = NameAndType #32:#33 // createStarted:()Lcom/google/common/base/Stopwatch; #35 = Methodref #31.#34 // com/google/common/base/Stopwatch.createStarted:()Lcom/google/common/base/Stopwatch; #36 = Utf8 invoke #37 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #38 = NameAndType #36:#37 // invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #39 = InterfaceMethodref #29.#38 // kotlin/jvm/functions/Function2.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #40 = Utf8 java/lang/StringBuilder #41 = Class #40 // java/lang/StringBuilder #42 = Utf8 <init> #43 = Utf8 ()V #44 = NameAndType #42:#43 // "<init>":()V #45 = Methodref #41.#44 // java/lang/StringBuilder."<init>":()V #46 = Utf8 call in ns: #47 = String #46 // call in ns: #48 = Utf8 append #49 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #50 = NameAndType #48:#49 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #51 = Methodref #41.#50 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #52 = Utf8 java/util/concurrent/TimeUnit #53 = Class #52 // java/util/concurrent/TimeUnit #54 = Utf8 NANOSECONDS #55 = Utf8 Ljava/util/concurrent/TimeUnit; #56 = NameAndType #54:#55 // NANOSECONDS:Ljava/util/concurrent/TimeUnit; #57 = Fieldref #53.#56 // java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; #58 = Utf8 elapsed #59 = Utf8 (Ljava/util/concurrent/TimeUnit;)J #60 = NameAndType #58:#59 // elapsed:(Ljava/util/concurrent/TimeUnit;)J #61 = Methodref #31.#60 // com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J #62 = Utf8 (J)Ljava/lang/StringBuilder; #63 = NameAndType #48:#62 // append:(J)Ljava/lang/StringBuilder; #64 = Methodref #41.#63 // java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; #65 = Utf8 toString #66 = Utf8 ()Ljava/lang/String; #67 = NameAndType #65:#66 // toString:()Ljava/lang/String; #68 = Methodref #41.#67 // java/lang/StringBuilder.toString:()Ljava/lang/String; #69 = Utf8 java/lang/System #70 = Class #69 // java/lang/System #71 = Utf8 out #72 = Utf8 Ljava/io/PrintStream; #73 = NameAndType #71:#72 // out:Ljava/io/PrintStream; #74 = Fieldref #70.#73 // java/lang/System.out:Ljava/io/PrintStream; #75 = Utf8 java/io/PrintStream #76 = Class #75 // java/io/PrintStream #77 = Utf8 println #78 = Utf8 (Ljava/lang/Object;)V #79 = NameAndType #77:#78 // println:(Ljava/lang/Object;)V #80 = Methodref #76.#79 // java/io/PrintStream.println:(Ljava/lang/Object;)V #81 = Utf8 watch$iv #82 = Utf8 Lcom/google/common/base/Stopwatch; #83 = Utf8 a$iv #84 = Utf8 Ljava/lang/Object; #85 = Utf8 b$iv #86 = Utf8 f$iv #87 = Utf8 Lkotlin/jvm/functions/Function2; #88 = Utf8 $i$f$timingLog #89 = Utf8 I #90 = Utf8 [Ljava/lang/String; #91 = Class #90 // "[Ljava/lang/String;" #92 = Utf8 java/lang/Throwable #93 = Class #92 // java/lang/Throwable #94 = Utf8 java/lang/String #95 = Class #94 // java/lang/String #96 = Utf8 timingLog #97 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; #98 = Utf8 f #99 = String #98 // f #100 = Utf8 kotlin/jvm/internal/InlineMarker #101 = Class #100 // kotlin/jvm/internal/InlineMarker #102 = Utf8 finallyStart #103 = Utf8 (I)V #104 = NameAndType #102:#103 // finallyStart:(I)V #105 = Methodref #101.#104 // kotlin/jvm/internal/InlineMarker.finallyStart:(I)V #106 = Utf8 finallyEnd #107 = NameAndType #106:#103 // finallyEnd:(I)V #108 = Methodref #101.#107 // kotlin/jvm/internal/InlineMarker.finallyEnd:(I)V #109 = Utf8 watch #110 = Utf8 a #111 = Utf8 b #112 = Utf8 Lkotlin/Metadata; #113 = Utf8 mv #114 = Integer 1 #115 = Utf8 bv #116 = Integer 0 #117 = Utf8 k #118 = Integer 2 #119 = Utf8 d1 #120 = Utf8 \n \n\n \n\n\n\n\n 0200¢LH\" \"\t\"2\nH2H\t2HH\tH0\rH¢¨ #121 = Utf8 d2 #122 = Utf8 #123 = Utf8 R #124 = Utf8 T #125 = Utf8 U #126 = Utf8 Lkotlin/Function2; #127 = Utf8 blog #128 = Utf8 Inlines.kt #129 = Utf8 Code #130 = Utf8 LocalVariableTable #131 = Utf8 LineNumberTable #132 = Utf8 StackMapTable #133 = Utf8 RuntimeInvisibleParameterAnnotations #134 = Utf8 Signature #135 = Utf8 <T:Ljava/lang/Object;U:Ljava/lang/Object;R:Ljava/lang/Object;>(TT;TU;Lkotlin/jvm/functions/Function2<-TT;-TU;+TR;>;)TR; #136 = Utf8 SourceFile #137 = Utf8 SourceDebugExtension #138 = Utf8 InnerClasses #139 = Utf8 RuntimeVisibleAnnotations { public static final void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL Code: stack=3, locals=8, args_size=1 0: aload_0 1: ldc #9 // String args 3: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V 6: iconst_1 7: invokestatic #21 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 10: astore_1 11: iconst_2 12: invokestatic #21 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 15: astore_2 16: getstatic #27 // Field pl/koziolekweb/blog/inlines/InlinesKt$main$1.INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; 19: checkcast #29 // class kotlin/jvm/functions/Function2 22: astore_3 23: nop 24: invokestatic #35 // Method com/google/common/base/Stopwatch.createStarted:()Lcom/google/common/base/Stopwatch; 27: astore 4 29: nop 30: aload_3 31: aload_1 32: aload_2 33: invokeinterface #39, 3 // InterfaceMethod kotlin/jvm/functions/Function2.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; 38: astore 5 40: new #41 // class java/lang/StringBuilder 43: dup 44: invokespecial #45 // Method java/lang/StringBuilder."<init>":()V 47: ldc #47 // String call in ns: 49: invokevirtual #51 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 52: aload 4 54: getstatic #57 // Field java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; 57: invokevirtual #61 // Method com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J 60: invokevirtual #64 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 63: invokevirtual #68 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 66: astore 6 68: nop 69: getstatic #74 // Field java/lang/System.out:Ljava/io/PrintStream; 72: aload 6 74: invokevirtual #80 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 77: aload 5 79: goto 124 82: astore 5 84: new #41 // class java/lang/StringBuilder 87: dup 88: invokespecial #45 // Method java/lang/StringBuilder."<init>":()V 91: ldc #47 // String call in ns: 93: invokevirtual #51 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 96: aload 4 98: getstatic #57 // Field java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; 101: invokevirtual #61 // Method com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J 104: invokevirtual #64 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 107: invokevirtual #68 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 110: astore 6 112: nop 113: getstatic #74 // Field java/lang/System.out:Ljava/io/PrintStream; 116: aload 6 118: invokevirtual #80 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 121: aload 5 123: athrow 124: pop 125: return Exception table: from to target type 29 40 82 any 82 84 82 any LocalVariableTable: Start Length Slot Name Signature 29 95 4 watch$iv Lcom/google/common/base/Stopwatch; 24 100 1 a$iv Ljava/lang/Object; 24 100 2 b$iv Ljava/lang/Object; 24 100 3 f$iv Lkotlin/jvm/functions/Function2; 24 100 7 $i$f$timingLog I 0 126 0 args [Ljava/lang/String; LineNumberTable: line 14: 6 line 19: 24 line 20: 29 line 21: 30 line 23: 40 line 23: 84 line 16: 125 StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 82 locals = [ class "[Ljava/lang/String;", class java/lang/Integer, class java/lang/Integer, class kotlin/jvm/functions/Function2, class com/google/common/base/Stopwatch ] stack = [ class java/lang/Throwable ] frame_type = 255 /* full_frame */ offset_delta = 41 locals = [ class "[Ljava/lang/String;", class java/lang/Integer, class java/lang/Integer, class kotlin/jvm/functions/Function2, class com/google/common/base/Stopwatch, class java/lang/Object, class java/lang/String ] stack = [ class java/lang/Object ] RuntimeInvisibleParameterAnnotations: 0: 0: #7() public static final <T extends java.lang.Object, U extends java.lang.Object, R extends java.lang.Object> R timingLog(T, U, kotlin.jvm.functions.Function2<? super T, ? super U, ? extends R>); descriptor: (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL Code: stack=3, locals=7, args_size=3 0: aload_2 1: ldc #99 // String f 3: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V 6: invokestatic #35 // Method com/google/common/base/Stopwatch.createStarted:()Lcom/google/common/base/Stopwatch; 9: astore 4 11: nop 12: aload_2 13: aload_0 14: aload_1 15: invokeinterface #39, 3 // InterfaceMethod kotlin/jvm/functions/Function2.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; 20: astore 5 22: iconst_1 23: invokestatic #105 // Method kotlin/jvm/internal/InlineMarker.finallyStart:(I)V 26: new #41 // class java/lang/StringBuilder 29: dup 30: invokespecial #45 // Method java/lang/StringBuilder."<init>":()V 33: ldc #47 // String call in ns: 35: invokevirtual #51 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 38: aload 4 40: getstatic #57 // Field java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; 43: invokevirtual #61 // Method com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J 46: invokevirtual #64 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 49: invokevirtual #68 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 52: astore 6 54: nop 55: getstatic #74 // Field java/lang/System.out:Ljava/io/PrintStream; 58: aload 6 60: invokevirtual #80 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 63: iconst_1 64: invokestatic #108 // Method kotlin/jvm/internal/InlineMarker.finallyEnd:(I)V 67: aload 5 69: areturn 70: astore 5 72: iconst_1 73: invokestatic #105 // Method kotlin/jvm/internal/InlineMarker.finallyStart:(I)V 76: new #41 // class java/lang/StringBuilder 79: dup 80: invokespecial #45 // Method java/lang/StringBuilder."<init>":()V 83: ldc #47 // String call in ns: 85: invokevirtual #51 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 88: aload 4 90: getstatic #57 // Field java/util/concurrent/TimeUnit.NANOSECONDS:Ljava/util/concurrent/TimeUnit; 93: invokevirtual #61 // Method com/google/common/base/Stopwatch.elapsed:(Ljava/util/concurrent/TimeUnit;)J 96: invokevirtual #64 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 99: invokevirtual #68 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 102: astore 6 104: nop 105: getstatic #74 // Field java/lang/System.out:Ljava/io/PrintStream; 108: aload 6 110: invokevirtual #80 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 113: iconst_1 114: invokestatic #108 // Method kotlin/jvm/internal/InlineMarker.finallyEnd:(I)V 117: aload 5 119: athrow Exception table: from to target type 11 22 70 any 70 72 70 any LocalVariableTable: Start Length Slot Name Signature 11 109 4 watch Lcom/google/common/base/Stopwatch; 0 120 0 a Ljava/lang/Object; 0 120 1 b Ljava/lang/Object; 0 120 2 f Lkotlin/jvm/functions/Function2; 0 120 3 $i$f$timingLog I LineNumberTable: line 19: 6 line 20: 11 line 21: 12 line 23: 26 line 23: 70 line 23: 76 StackMapTable: number_of_entries = 1 frame_type = 255 /* full_frame */ offset_delta = 70 locals = [ class java/lang/Object, class java/lang/Object, class kotlin/jvm/functions/Function2, top, class com/google/common/base/Stopwatch ] stack = [ class java/lang/Throwable ] Signature: #135 // <T:Ljava/lang/Object;U:Ljava/lang/Object;R:Ljava/lang/Object;>(TT;TU;Lkotlin/jvm/functions/Function2<-TT;-TU;+TR;>;)TR; RuntimeInvisibleParameterAnnotations: 0: 1: 2: 0: #7() } SourceFile: "Inlines.kt" SourceDebugExtension: SMAP Inlines.kt Kotlin *S Kotlin *F + 1 Inlines.kt pl/koziolekweb/blog/inlines/InlinesKt *L 1#1,28:1 *E InnerClasses: static final #23; //class pl/koziolekweb/blog/inlines/InlinesKt$main$1 RuntimeVisibleAnnotations: 0: #112(#113=[I#114,I#114,I#114],#115=[I#114,I#116,I#116],#117=I#118,#119=[s#120],#121=[s#5,s#122,s#8,s#122,s#122,s#6,s#96,s#123,s#124,s#125,s#110,s#111,s#98,s#126,s#97,s#127])
W linii 174 mamy wywołanie lambdy oddelegowane do InlinesKt$main$1, utworzonego w linii 164. I jeszcze kod InlinesKt$main$1:
Listing 8. Kod dodatkowo wygenerowanej klasy wewnętrznej
Classfile /home/koziolek/workspace/oldone/blog_old/target/classes/pl/koziolekweb/blog/inlines/InlinesKt$main$1.class Last modified 2016-05-23; size 1280 bytes MD5 checksum ab966e9707066ff871fe25438fd2debb Compiled from "Inlines.kt" final class pl.koziolekweb.blog.inlines.InlinesKt$main$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function2<java.lang.Integer, java.lang.Integer, java.lang.Integer> minor version: 0 major version: 50 flags: ACC_FINAL, ACC_SUPER Constant pool: #1 = Utf8 pl/koziolekweb/blog/inlines/InlinesKt$main$1 #2 = Class #1 // pl/koziolekweb/blog/inlines/InlinesKt$main$1 #3 = Utf8 Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function2<Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;>; #4 = Utf8 kotlin/jvm/internal/Lambda #5 = Class #4 // kotlin/jvm/internal/Lambda #6 = Utf8 kotlin/jvm/functions/Function2 #7 = Class #6 // kotlin/jvm/functions/Function2 #8 = Utf8 invoke #9 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #10 = Utf8 java/lang/Number #11 = Class #10 // java/lang/Number #12 = Utf8 intValue #13 = Utf8 ()I #14 = NameAndType #12:#13 // intValue:()I #15 = Methodref #11.#14 // java/lang/Number.intValue:()I #16 = Utf8 (II)I #17 = NameAndType #8:#16 // invoke:(II)I #18 = Methodref #2.#17 // pl/koziolekweb/blog/inlines/InlinesKt$main$1.invoke:(II)I #19 = Utf8 java/lang/Integer #20 = Class #19 // java/lang/Integer #21 = Utf8 valueOf #22 = Utf8 (I)Ljava/lang/Integer; #23 = NameAndType #21:#22 // valueOf:(I)Ljava/lang/Integer; #24 = Methodref #20.#23 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #25 = Utf8 this #26 = Utf8 Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #27 = Utf8 a #28 = Utf8 I #29 = Utf8 b #30 = Utf8 <init> #31 = Utf8 ()V #32 = Utf8 (I)V #33 = NameAndType #30:#32 // "<init>":(I)V #34 = Methodref #5.#33 // kotlin/jvm/internal/Lambda."<init>":(I)V #35 = Utf8 INSTANCE #36 = Utf8 <clinit> #37 = Utf8 Lkotlin/Metadata; #38 = Utf8 mv #39 = Integer 1 #40 = Utf8 bv #41 = Integer 0 #42 = Utf8 k #43 = Integer 3 #44 = Utf8 d1 #45 = Utf8 \n\n \n\n 02020H\n¢ #46 = Utf8 d2 #47 = Utf8 <anonymous> #48 = Utf8 #49 = Utf8 pl/koziolekweb/blog/inlines/InlinesKt #50 = Class #49 // pl/koziolekweb/blog/inlines/InlinesKt #51 = Utf8 main #52 = Utf8 ([Ljava/lang/String;)V #53 = NameAndType #51:#52 // main:([Ljava/lang/String;)V #54 = NameAndType #30:#31 // "<init>":()V #55 = Methodref #2.#54 // pl/koziolekweb/blog/inlines/InlinesKt$main$1."<init>":()V #56 = NameAndType #35:#26 // INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #57 = Fieldref #2.#56 // pl/koziolekweb/blog/inlines/InlinesKt$main$1.INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; #58 = Utf8 Inlines.kt #59 = Utf8 Code #60 = Utf8 LocalVariableTable #61 = Utf8 LineNumberTable #62 = Utf8 Signature #63 = Utf8 SourceFile #64 = Utf8 EnclosingMethod #65 = Utf8 InnerClasses #66 = Utf8 RuntimeVisibleAnnotations { public static final pl.koziolekweb.blog.inlines.InlinesKt$main$1 INSTANCE; descriptor: Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL public java.lang.Object invoke(java.lang.Object, java.lang.Object); descriptor: (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=3, locals=3, args_size=3 0: aload_0 1: aload_1 2: checkcast #11 // class java/lang/Number 5: invokevirtual #15 // Method java/lang/Number.intValue:()I 8: aload_2 9: checkcast #11 // class java/lang/Number 12: invokevirtual #15 // Method java/lang/Number.intValue:()I 15: invokevirtual #18 // Method invoke:(II)I 18: invokestatic #24 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 21: areturn public final int invoke(int, int); descriptor: (II)I flags: ACC_PUBLIC, ACC_FINAL Code: stack=2, locals=3, args_size=3 0: iload_1 1: iload_2 2: iadd 3: ireturn LocalVariableTable: Start Length Slot Name Signature 0 4 0 this Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; 0 4 1 a I 0 4 2 b I LineNumberTable: line 14: 0 pl.koziolekweb.blog.inlines.InlinesKt$main$1(); descriptor: ()V flags: Code: stack=2, locals=1, args_size=1 0: aload_0 1: iconst_2 2: invokespecial #34 // Method kotlin/jvm/internal/Lambda."<init>":(I)V 5: return static {}; descriptor: ()V flags: ACC_STATIC Code: stack=2, locals=0, args_size=0 0: new #2 // class pl/koziolekweb/blog/inlines/InlinesKt$main$1 3: dup 4: invokespecial #55 // Method "<init>":()V 7: putstatic #57 // Field INSTANCE:Lpl/koziolekweb/blog/inlines/InlinesKt$main$1; 10: return } Signature: #3 // Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function2<Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;>; SourceFile: "Inlines.kt" EnclosingMethod: #50.#53 // pl.koziolekweb.blog.inlines.InlinesKt.main InnerClasses: static final #2; //class pl/koziolekweb/blog/inlines/InlinesKt$main$1 RuntimeVisibleAnnotations: 0: #37(#38=[I#39,I#39,I#39],#40=[I#39,I#41,I#41],#42=I#43,#44=[s#45],#46=[s#47,s#48,s#27,s#29,s#8])
Podsumowanie
Jak zwykle, coś za coś. Z jednej strony mechanizm powiązany z inliningiem pozwala na stworzenie szybszego kodu, pozbawionego narzutu związanego z wywoływaniem warstw pośredniczących. Z drugiej strony taki kod będzie miał większy rozmiar. Nie jest to bez znaczenia w przypadku aplikacji mobilnych. Dla ciekawskich zadanie ustawcie debugger na wywołaniu funkcji f w timingLog i zobaczcie, jak wygląda stos wywołań w obu przypadkach.