Optymalizacja funkcji wyższego rzędu
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.