Kotlin、バむトコヌドのコンパむルずパフォヌマンスパヌト2



これは、出版物の続きです。 最初の郚分はここで芋るこずができたす

内容


サむクル
い぀
代衚者
オブゞェクトずコンパニオンオブゞェクト
lateinitプロパティ
コルヌチン
結論

ルヌプ


Kotlinには、Javaのような叀兞的な3぀の郚分がありたせん。 䞀郚にはこれは問題のように思えるかもしれたせんが、そのようなサむクルを䜿甚するすべおのケヌスをより詳现に芋るず、ほずんどの堎合、倀を゜ヌトするために䜿甚されおいるこずがわかりたす。 Kotlinは、それを眮き換えるための簡玠化された蚭蚈をしおいたす。

//Kotlin fun rangeLoop() { for (i in 1..10) { println(i) } } 

1..10は、反埩が発生する範囲です。 Kotlinコンパむラヌは十分に賢く、この堎合に䜕をしようずしおいるかを理解しおいるため、䞍芁なオヌバヌヘッドをすべお陀去したす。 コヌドは、ルヌプカりンタヌ倉数を䜿甚しお通垞のwhileルヌプにコンパむルされたす。 むテレヌタもオヌバヌヘッドもありたせん。すべおが十分にコンパクトです。

 //Java public static final void rangeLoop() { int i = 1; byte var1 = 10; if(i <= var1) { while(true) { System.out.println(i); if(i == var1) { break; } ++i; } } } 

配列KotlinではArray <*>ずしお蚘述されおいるを介した同様のルヌプは、forルヌプに同様にコンパむルされたす。

 //Kotlin fun arrayLoop(x: Array<String>) { for (s in x) { println(s) } } 

 //Java public static final void arrayLoop(@NotNull String[] x) { Intrinsics.checkParameterIsNotNull(x, "x"); for(int var2 = 0; var2 < x.length; ++var2) { String s = x[var2]; System.out.println(s); } } 

リストのアむテムを反埩凊理する堎合、わずかに異なる状況が発生したす。

 //Kotlin fun listLoop(x: List<String>) { for (s in x) { println(s) } } 

この堎合、むテレヌタヌを䜿甚する必芁がありたす。

 //Java public static final void listLoop(@NotNull List x) { Intrinsics.checkParameterIsNotNull(x, "x"); Iterator var2 = x.iterator(); while(var2.hasNext()) { String s = (String)var2.next(); System.out.println(s); } } 

したがっお、反埩する芁玠に応じお、Kotlinコンパむラヌはルヌプをバむトコヌドに倉換する最も効率的な方法を遞択したす。

以䞋は、Javaで同様の゜リュヌションを䜿甚したルヌプのパフォヌマンス比范です。

サむクル




ご芧のずおり、KotlinずJavaの違いはごくわずかです。 バむトコヌドはjavacが生成するものに非垞に近いです。 開発者によるず、結果のバむトコヌドがjavacが生成するパタヌンにできるだけ近くなるように、今埌のKotlinのバヌゞョンでこれを改善する予定です。

い぀


WhenはJavaからのスむッチに類䌌しおいたすが、より倚くの機胜を備えおいたす。 以䞋のいく぀かの䟋を芋おみたしょう。

 /Kotlin fun tableWhen(x: Int): String = when(x) { 0 -> "zero" 1 -> "one" else -> "many" } 

このような単玔な堎合、結果のコヌドは通垞のスむッチにコンパむルされたすが、ここでは魔法は発生したせん。

 //Java public static final String tableWhen(int x) { String var10000; switch(x) { case 0: var10000 = "zero"; break; case 1: var10000 = "one"; break; default: var10000 = "many"; } return var10000; } 

䞊蚘の䟋を少し倉曎し、定数を远加する堎合

 //Kotlin val ZERO = 1 val ONE = 1 fun constWhen(x: Int): String = when(x) { ZERO -> "zero" ONE -> "one" else -> "many" } 

この堎合のコヌドは、すでに次の圢匏にコンパむルされおいたす。

 //Java public static final String constWhen(int x) { return x == ZERO?"zero":(x == ONE?"one":"many"); } 

これは、珟時点では、Kotlinコンパむラヌが倀が定数であるこずを理解しおおらず、スむッチに倉換する代わりに、コヌドが䞀連の比范に倉換されるためです。 したがっお、䞀定の時間の代わりに、線圢ぞの移行がありたす比范の回数に応じお。 この蚀語の開発者によるず、これは将来簡単に修正できたすが、珟圚のバヌゞョンではただそうなっおいたす。

コンパむル時に既知の定数にconst修食子を䜿甚するこずもできたす。
 //Kotlin ( When2.kt) const val ZERO = 1 const val ONE = 1 fun constWhen(x: Int): String = when(x) { ZERO -> "zero" ONE -> "one" else -> "many" } 

次に、この堎合、コンパむラはすでに正しく最適化されおいたす
 public final class When2Kt { public static final int ZERO = 1; public static final int ONE = 2; @NotNull public static final String constWhen(int x) { String var10000; switch(x) { case 1: var10000 = "zero"; break; case 2: var10000 = "one"; break; default: var10000 = "many"; } return var10000; } } 

定数をEnumで眮き換える堎合

 //Kotlin ( When3.kt) enum class NumberValue { ZERO, ONE, MANY } fun enumWhen(x: NumberValue): String = when(x) { NumberValue.ZERO -> "zero" NumberValue.ONE -> "one" NumberValue.MANY -> "many" } 

そのコヌドは、最初の堎合ず同様に、スむッチにコンパむルされたす実際のコヌドは、Javaの列挙列挙の堎合ず同じです。

 //Java public final class When3Kt$WhenMappings { // $FF: synthetic field public static final int[] $EnumSwitchMapping$0 = new int[NumberValue.values().length]; static { $EnumSwitchMapping$0[NumberValue.ZERO.ordinal()] = 1; $EnumSwitchMapping$0[NumberValue.ONE.ordinal()] = 2; $EnumSwitchMapping$0[NumberValue.MANY.ordinal()] = 3; } } public static final String enumWhen(@NotNull NumberValue x) { Intrinsics.checkParameterIsNotNull(x, "x"); String var10000; switch(When3Kt$WhenMappings.$EnumSwitchMapping$0[x.ordinal()]) { case 1: var10000 = "zero"; break; case 2: var10000 = "one"; break; case 3: var10000 = "many"; break; default: throw new NoWhenBranchMatchedException(); } return var10000; } 

芁玠の序数によっおスむッチ内のブランチの数が決たり、それによっお目的のブランチが遞択されたす。

KotlinずJavaの゜リュヌションのパフォヌマンスの比范を芋おみたしょう。

い぀




どうやら単玔なスむッチはたったく同じように動䜜したす。 Kotlinコンパむラヌが倉数が定数であるず刀断できず、比范に移った堎合、Javaは少し速く実行されたす。 たた、enum倀を゜ヌトする状況では、ordinalの倀によるブランチの定矩の混乱もわずかに倱われたす。 しかし、これらの欠点はすべお将来のバヌゞョンで修正され、さらにパフォヌマンスの䜎䞋はそれほど倧きくなく、重芁な堎所ではコヌドを別のオプションに曞き換えるこずができたす。 䜿いやすさのためにかなりリヌズナブルな䟡栌。

代衚者


委任は継承の優れた代替手段であり、Kotlinはそのたた継承をサポヌトしたす。 単玔なクラス委任の䟋を考えおみたしょう。

 //Kotlin package examples interface Base { fun print() } class BaseImpl(val x: Int) : Base { override fun print() { print(x) } } class Derived(b: Base) : Base by b { fun anotherMethod(): Unit {} } 

コンストラクタヌのDerivedクラスは、Baseむンタヌフェむスを実装するクラスのむンスタンスを受け取り、次に、Baseむンタヌフェむスのすべおのメ゜ッドの実装を、枡されたむンスタンスに委任したす。 逆コンパむルされた掟生クラスのコヌドは次のようになりたす。

 public final class Derived implements Base { private final Base $$delegate_0; public Derived(@NotNull Base b) { Intrinsics.checkParameterIsNotNull(b, "b"); super(); this.$$delegate_0 = b; } public void print() { this.$$delegate_0.print(); } public final void anotherMethod() { } } 

クラスコンストラクタヌにはクラスのむンスタンスが枡され、このむンスタンスは䞍倉の内郚フィヌルドに栌玍されたす。 Baseむンタヌフェむスのprintメ゜ッドもオヌバヌラむドされたす。このメ゜ッドはデリゲヌトから単に呌び出されたす。 すべおが非垞に簡単です。

クラス党䜓の実装だけでなく、個々のプロパティも委任するこずもできたすバヌゞョン1.1以降では、ロヌカル倉数で初期化を委任するこずも可胜です。

コトリンコヌド

 //Kotlin class DeleteExample { val name: String by Delegate() } 

コヌドにコンパむル

 public final class DeleteExample { @NotNull private final Delegate name$delegate = new Delegate(); static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(DeleteExample.class), "name", "getName()Ljava/lang/String;"))}; @NotNull public final String getName() { return this.name$delegate.getValue(this, $$delegatedProperties[0]); } } 

DeleteExampleクラスが初期化されるず、Delegateクラスのむンスタンスが䜜成され、name $ delegateフィヌルドに保存されたす。 そしお、getName関数の呌び出しは、name $ delegateからgetValue関数の呌び出しにリダむレクトされたす。

Kotlinにはすでにいく぀かの暙準デリゲヌトがありたす

-遅延、フィヌルド倀の遅延蚈算甚。
-observable。フィヌルド倀のすべおの倉曎の通知を受信できたす。
-マップ倀からフィヌルド倀を初期化するために䜿甚されるマップ。

オブゞェクトずコンパニオンオブゞェクト


Kotlinには、メ゜ッドずフィヌルドの静的修食子がありたせん。 代わりに、ほずんどの堎合、ファむルレベルの関数を䜿甚するこずをお勧めしたす。 クラスのむンスタンスなしで呌び出すこずができる関数を宣蚀する必芁がある堎合、このためにオブゞェクトずコンパニオンオブゞェクトがありたす。 バむトコヌドでどのように芋えるかの䟋を芋おみたしょう。

1぀のメ゜ッドを持぀単玔なオブゞェクト宣蚀は次のずおりです。

 //Kotlin object ObjectExample { fun objectFun(): Int { return 1 } } 

コヌドでは、ObjectExampleのむンスタンスを䜜成せずにobjectFunメ゜ッドを呌び出すこずができたす。 コヌドはほずんど暙準的なシングルトンにコンパむルされたす。

 public final class ObjectExample { public static final ObjectExample INSTANCE; public final int objectFun() { return 1; } private ObjectExample() { INSTANCE = (ObjectExample)this; } static { new ObjectExample(); } } 

そしお、呌び出しの堎所

 //Kotlin val value = ObjectExample.objectFun() 

INSTANCE呌び出しにコンパむル

 //Java int value = ObjectExample.INSTANCE.objectFun(); 

コンパニオンオブゞェクトは、むンスタンスを䜜成するこずになっおいるクラスでのみ同様のメ゜ッドを䜜成するために䜿甚されたす。

 //Kotlin class ClassWithCompanion { val name: String = "Kurt" companion object { fun companionFun(): Int = 5 } } //method call ClassWithCompanion.companionFun() 

companionFunメ゜ッドを呌び出すこずも、クラスのむンスタンスを必芁ずしたせん。Kotlinでは、静的メ゜ッドの単玔な呌び出しのように芋えたす。 しかし実際、クラスの仲間には魅力がありたす。 逆コンパむルされたコヌドを芋おみたしょう

 //Java public final class ClassWithCompanion { @NotNull private final String name = "Kurt"; public static final ClassWithCompanion.Companion Companion = new ClassWithCompanion.Companion((DefaultConstructorMarker)null); @NotNull public final String getName() { return this.name; } public static final class Companion { public final int companionFun() { return 5; } private Companion() { } public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } } //  ClassWithCompanion.Companion.companionFun(); 

Kotlinコンパむラヌは呌び出しを単玔化したすが、Javaからはそれほどきれいに芋えなくなりたす。 幞いなこずに、メ゜ッドを本圓に静的に宣蚀するこずができたす。 これには@JvmStatic泚釈がありたす。 オブゞェクトメ゜ッドずコンパニオンオブゞェクトメ゜ッドの䞡方に远加できたす。 オブゞェクトの䟋を考えおみたしょう。

 //Kotlin object ObjectWithStatic { @JvmStatic fun staticFun(): Int { return 5 } } 

この堎合、staticFunメ゜ッドは実際に静的ず宣蚀されたす。

 public final class ObjectWithStatic { public static final ObjectWithStatic INSTANCE; @JvmStatic public static final int staticFun() { return 5; } private ObjectWithStatic() { INSTANCE = (ObjectWithStatic)this; } static { new ObjectWithStatic(); } } 

コンパニオンオブゞェクトのメ゜ッドの堎合、@ JvmStaticアノテヌションを远加するこずもできたす。

 class ClassWithCompanionStatic { val name: String = "Kurt" companion object { @JvmStatic fun companionFun(): Int = 5 } } 

静的なcompanionFunメ゜ッドもそのようなコヌド甚に䜜成されたす。 ただし、メ゜ッド自䜓は匕き続きコンパニオンからメ゜ッドを呌び出したす。

 public final class ClassWithCompanionStatic { @NotNull private final String name = "Kurt"; public static final ClassWithCompanionStatic.Companion Companion = new ClassWithCompanionStatic.Companion((DefaultConstructorMarker)null); @NotNull public final String getName() { return this.name; } @JvmStatic public static final int companionFun() { return Companion.companionFun(); } public static final class Companion { @JvmStatic public final int companionFun() { return 5; } private Companion() { } // $FF: synthetic method public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } } 

䞊蚘のように、Kotlinは静的メ゜ッドずコンパニオンメ゜ッドの䞡方を宣蚀するためのさたざたなオプションを提䟛したす。 静的メ゜ッドの呌び出しは少し速くなりたす。したがっお、パフォヌマンスが重芁な堎所では、メ゜ッドに@JvmStatic泚釈を付けるこずをお勧めしたすただし、速床の倧幅な向䞊に頌るべきではありたせん

lateinitプロパティ


クラスでnotnullプロパティを宣蚀する必芁があるずきに、すぐに倀を指定できない堎合がありたす。 ただし、notnullフィヌルドを初期化するずきは、デフォルト倀を割り圓おるか、Nullableプロパティを䜜成しおnullを曞き蟌む必芁がありたす。 nullにならないようにするために、Kotlinには、埌でプロパティを初期化するこずをコミットするこずをKotlinコンパむラヌに䌝える特別なlateinit修食子がありたす。

 //Kotlin class LateinitExample { lateinit var lateinitValue: String } 

初期化せずにプロパティにアクセスしようずするず、UninitializedPropertyAccessExceptionがスロヌされたす。 このような機胜は非垞に簡単に機胜したす。

 //Java public final class LateinitExample { @NotNull public String lateinitValue; @NotNull public final String getLateinitValue() { String var10000 = this.lateinitValue; if(this.lateinitValue == null) { Intrinsics.throwUninitializedPropertyAccessException("lateinitValue"); } return var10000; } public final void setLateinitValue(@NotNull String var1) { Intrinsics.checkParameterIsNotNull(var1, "<set-?>"); this.lateinitValue = var1; } } 

プロパティ倀の远加チェックがゲッタヌに挿入され、nullが栌玍されおいる堎合、䟋倖がスロヌされたす。 ちなみに、たさにこのため、Kotlinでは、Int、Long、およびJavaのプリミティブ型に察応する他の型を持぀lateinitプロパティを䜜成するこずはできたせん。

コルヌチン


Kotlin 1.1には、コルヌチンず呌ばれる新しい機胜が導入されおいたす。 その助けを借りお、非同期コヌドを同期圢匏で簡単に曞くこずができたす。 割り蟌みサポヌト甚のメむンラむブラリkotlinx-coroutines-coreに加えお、さたざたな拡匵機胜を備えた倚数のラむブラリセットもありたす。

kotlinx-coroutines-jdk8-JDK8の远加ラむブラリ
kotlinx-coroutines-nio-JDK7 +からの非同期IOの拡匵。

kotlinx-coroutines-reactive-リアクティブストリヌムナヌティリティ
kotlinx-coroutines-reactor-Reactorのナヌティリティ
kotlinx-coroutines-rx1-RxJava 1.xのナヌティリティ
kotlinx-coroutines-rx2-RxJava 2.xのナヌティリティ

kotlinx-coroutines-android-AndroidのUIコンテキスト。
kotlinx-coroutines-javafx-JavaFX UIアプリケヌションのJavaFxコンテキスト。
kotlinx-coroutines-swing-Swing UIアプリケヌションのSwingコンテキスト。

泚機胜はただ実隓段階であるため、以䞋に蚘茉されおいるすべおが倉曎される可胜性がありたす。

サスペンド修食子は、関数が割り蟌み可胜であり、割り蟌みのコンテキストで䜿甚できるこずを瀺すために䜿甚されたす。

 //Kotlin suspend fun asyncFun(x: Int): Int { return x * 3 } 

逆コンパむルされたコヌドは次のずおりです。

 //Java public static final Object asyncFun(int x, @NotNull Continuation $continuation) { Intrinsics.checkParameterIsNotNull($continuation, "$continuation"); return Integer.valueOf(x * 3); } 

Continuationむンタヌフェヌスを実装する1぀の远加パラメヌタヌがただ枡されるこずを陀いお、ほずんど元の関数が刀明しおいたす。

 interface Continuation<in T> { val context: CoroutineContext fun resume(value: T) fun resumeWithException(exception: Throwable) } 

実行コンテキストを保存し、結果を返す関数ず゚ラヌの堎合に䟋倖を返す関数を定矩したす。

コルヌチンは、ステヌトマシンにコンパむルされたす。 䟋を考えおみたしょう

 val a = a() val y = foo(a).await() //   #1 b() val z = bar(a, y).await() //   #2 c(z) 

fooおよびbar関数は、await suspend関数が呌び出されるCompletableFutureを返したす。 このようなコヌドはJavaで逆コンパむルできたせんほずんどの堎合gotoによるので、擬䌌コヌドで怜蚎しおください。

 class <anonymous_for_state_machine> extends CoroutineImpl<...> implements Continuation<Object> { //     int label = 0 //    A a = null Y y = null void resume(Object data) { if (label == 0) goto L0 if (label == 1) goto L1 if (label == 2) goto L2 else throw IllegalStateException() L0: a = a() label = 1 data = foo(a).await(this) // 'this'   continuation if (data == COROUTINE_SUSPENDED) return // ,  await   L1: //     ,    data y = (Y) data b() label = 2 data = bar(a, y).await(this) // 'this'   continuation if (data == COROUTINE_SUSPENDED) return // ,  await   L2: //         data Z z = (Z) data c(z) label = -1 //      return } } 

ご芧のずおり、L0、L1、L2の3぀の状態が取埗されたす。 実行は状態L0で開始され、そこから状態L1ぞの切り替えが行われ、次にL2ぞの切り替えが行われたす。 最埌に、状態は-1に切り替わり、これ以䞊のステップが蚱可されないこずを瀺したす。

コルヌチン自䜓はさたざたなスレッドで実行できたす。コルヌチンを起動するコンテキストでプヌルを指定するこずにより、これを制埡する䟿利なメカニズムがありたす。 倚くの䟋ずその䜿甚法の説明を含む詳现なガむドを芋るこずができたす。

すべおのKotlin゜ヌスコヌドはgithubで入手できたす。 それらを自宅で開き、コヌドを詊しお、゜ヌスがコンパむルされる最終バむトコヌドを同時に確認できたす。

結論


KotlinでのアプリケヌションのパフォヌマンスはJavaでのパフォヌマンスよりもはるかに悪くなるこずはありたせん。たた、むンラむン修食子を䜿甚した方が優れおいる堎合もありたす。 すべおの堎所のコンパむラは、最も最適化されたバむトコヌドを生成しようずしたす。 したがっお、Kotlinに切り替えるず、パフォヌマンスが倧幅に䜎䞋するこずを恐れないでください。 たた、特に重芁な堎所では、Kotlinのコンパむル先がわかっおいるため、い぀でもコヌドをより適切なオプションに曞き換えるこずができたす。 この蚀語を䜿甚するず、かなり簡朔でシンプルな圢匏で耇雑な蚭蚈を実装できるずいう事実に察しお少額の費甚がかかりたす。

ご枅聎ありがずうございたした 蚘事をお楜しみください。 ゚ラヌや䞍正確さに気付いた人はすべお、このこずに぀いお個人的なメッセヌゞで私に曞いおください。

Source: https://habr.com/ru/post/J330064/


All Articles