R8/Proguard: Class Mergingについて

コードの最適化の話です。この記事ではClass Mergingについて紹介します。

Class Merging?

その名の通り、Classをマージする最適化です。最終的なクラス数減らすことが期待出来ます。 Class Mergingには縦方向(Vertical)と、横方向(Horizontal)があります。

まずは縦方向のClass Mergingについて説明します。

縦方向?

縦方向とはスーパータイプの実装が1つだったときに、そのスーパータイプと実装であるサブタイプを1つにまとめる最適化です。

例えば、次の実装は最適化によって1つにまとめられます。

interface IVertical {
    fun show(i: Int)
}

class Vertical(
    private val a: Int
) : IVertical {
    override fun show(i: Int) {
        println("start called $i $a")
    }
}

–> Proguard/R8による最適化後 –>

public final class Vertical {
    ...

    public final void show(int i) {
        ...
    }
}

IVerticalインターフェースが見事に消されていることが分かります。

また、インターフェースではなくabstractクラスの場合はR8の場合のみ上手くマージされました。

次に横方向のマージを紹介します。

横方向?

Staticメソッドのみを持つクラスを1つにまとめるなどの最適化を行います。

例えばKotlinの複数のファイルで定義されたトップレベル関数を1つのクラスにまとめてくれます。

// ShowExt.kt
fun Int.show1(i: Int) {
    ...
}

// ShowExt2.kt
fun Int.show2(i: Int) {
    ...
}

--> Proguard/R8による最適化後 -->

```java
public class ShowExt2Kt {
    public static final void show1(int i) {
        ...
    }
}

    public static final void show2(int i) {
        ...
    }
}

1つのクラスにまとめられていることが分かります。

また、実際のAndroidプロジェクトで確認したところ、確かに拡張関数が他のクラスにマージされていました。

// Fuga.kt
fun Int.show1() {
    ...
}

// mapping.txtの内容
android.support.constraint.solver.widgets.Analyzer -> a.b.b.a.a.a:
    ...
    void com.github.satoshun.example.FugaKt.show1(int) -> d

なぜこのクラスに移動したかは分からないので、今後の宿題とします。申し訳ありません😫

まとめ

Written by