every Tech Blog

株式会社エブリーのTech Blogです。

Android プロジェクトの KSP 化を検討するにあたって

この記事は every Tech Blog Advent Calendar 2024(夏) 20 日目の記事です。

はじめに

こんにちは、DELISH KITCHEN でクライアントエンジニアを担当している岡田です。
今回は KSP 化について執筆させていただきます。

概要

Kotlin でより効率的な開発を行う上で、アノテーション処理は欠かせない要素です。アノテーションを利用することで、定型的なコードを自動生成したり、コンパイル時にコードの検証を行ったりすることができます。

Kotlin のアノテーション処理といえば、従来は kapt が主流です。しかし、近年ではより高速でKotlinの言語機能を活かせる KSP が主流になりつつあります。

この記事では、kapt と KSP について調べ、 Android プロジェクトの KSP 化を検討するにあたって必要な情報を記述します。

kapt について

kapt (Kotlin Annotation Processing Tool) を使用すると、 Java アノテーションプロセッサを使用して Kotlin コードを処理できます。
Room などの Android 開発でよく使用される、多くのライブラリと連携しています。

しかし、 Kotlin ファイルから Java アノテーションプロセッサが読み取れるようにするためには Java スタブを生成する必要があります。
実際に生成された Java スタブ は build/generated/source/kapt/ で確認することができます。
例えば以下は実際に DELISH KITCHEN で Hilt によって生成される、 ArticleListActivity のインスタンスに依存性を注入するためのコードです。

ArticleListActivity_GeneratedInjector.java

@OriginatingElement(
    topLevelClass = ArticleListActivity.class
)
@GeneratedEntryPoint
@InstallIn(ActivityComponent.class)
public interface ArticleListActivity_GeneratedInjector {
  void injectArticleListActivity(ArticleListActivity articleListActivity);
}

.*_GeneratedInjector.java@AndroidEntryPoint@Inject など、依存性を注入する必要がある全箇所に対して生成されます。
この例のような Java スタブが、 Hilt に関してのみでもプロジェクト内で相当な数生成されています。
Java スタブの生成は高コストのオペレーションのため、処理速度が遅く、ビルド速度に大きく影響してしまうのです。

また 2024/6/20 時点で kapt はメンテナンスモードに入っています。
「既に新しい機能が実装されることはない」と明記されており、 KSP の使用が推奨されています。

KSP がサポートされていないライブラリを用いたいなどの理由がない限りは、 kapt ではなく KSP を使用する方が良いでしょう。

KSP について

KSP (Kotlin Symbol Processing) は、 Kotlin コードを直接処理するためのアノテーション処理ツールです。
kapt とは異なり、 Kotlin コンパイラのプラグインとして動作するため、 Java コードへの変換が不要です。
つまり kapt のように Java スタブを生成する必要がなくなり、処理速度が大幅に上昇し、ビルド速度も速くなります。

Googleが公開しているGithubによると、「 kapt と比較すると、 KSP を使用するアノテーションプロセッサは最大 2 倍高速に実行できる」そうです。
また Kotlin の公式ドキュメントでは、コード生成に 7.5 倍ほどのパフォーマンス差があることも確認されています。

For performance evaluation, we implemented a simplified version of Glide in KSP to make it generate code for the Tachiyomi project. While the total Kotlin compilation time of the project is 21.55 seconds on our test device, it took 8.67 seconds for kapt to generate the code, and it took 1.15 seconds for our KSP implementation to generate the code.

仕組みとして、 Kotlin プログラムを Kotlin の文法に沿ってシンボルレベルでモデル化しているそうです。
KSP は Kotlin プログラムの構造を、クラスや関数などの構成要素単位で処理することができます。
Kotlin プログラムの構造をより抽象化された形で捉えて処理することで、アノテーション処理などのタスクを効率的に行います。
ただし、 if 文や for ループなどの制御構文にはアクセスできず、細かい制御フローの解析には向いていません。

kapt から KSP へ移行する方法は、公式ドキュメントを参照ください。
また KSP の最新のリリースはこちらから確認できます。
今後のロードマップには、マルチプラットフォーム対応やパフォーマンス改善も挙げられています。

実際の速度比較

では実際に KSP 化すると、 Kotlin のタスクはどの程度速度が上がるのでしょうか。
今回は簡単に、 Room でデータを Insert するだけの簡単なアプリを作成して比較しました。
測定方法は Android Studio の Build > Clean Project 実行後、 Build > Rebuild Project を実行し、 Build Analyzer にて測定しました。 データはそれぞれ 10 回ずつ測定し、画像は平均に一番近いデータを添付しています。

kapt KSP
1.9s 1.4s

結果として、およそ 0.5s ほど KSP の方が早かったです。 kapt で生成された Java スタブとして、以下のファイルが確認できました。

  • AppDatabase_Impl.java
  • UserDao_Impl.java

これらは Room の Database と Dao 関連のスタブです。たった 2 ファイルの生成ですが、如実な差を確認することができました。
Room や Hilt を導入しているプロジェクトでは、大幅な速度上昇が見込めそうです。

KSP の注意点

kapt と KSP の併用する際は注意が必要です。

Room などの主要なライブラリは KSP に対応していますが、 KSP に対応していないライブラリも存在します。
こちらで現在サポートされている主要ライブラリを確認することが可能です。
例えば 2024/6/20 時点で、 Auto Factory は KSP をサポートしていません。
またプロジェクトのライブラリ更新が滞っており、そちらを先に対応しないと KSP 化できないという場合も想定できます。
プロジェクトでこれらのような KSP に未対応のライブラリを導入している場合は、一部のみを KSP 化し、 kapt と併用する形になるでしょう。

しかし kapt と KSP の併用は、ビルド時間が増加することがあります。
これは同じアノテーションプロセッサを kapt と KSP の両方で実行する場合に処理が重複してしまうためです。
高速なアノテーション処理を期待して KSP を導入したのに、逆に処理が遅くなるという事態に陥ります。

まとめ

この記事では、 Kotlin のアノテーション処理ツールである kapt と KSP について記述しました。

kapt は Java のアノテーションプロセッサを Kotlin で利用できるツールですが、 Javaスタブ の生成が必要なためビルド時間が長くなるという欠点があります。
また、現在メンテナンスモードに入っているため、今後 KSP への移行が推奨されています。

KSP は Kotlin コードを直接処理できるため、 kapt よりも高速にアノテーション処理を行うことができます。
公式ドキュメントによると、 kapt と比較して最大2倍、コード生成では 7.5 倍ほどのパフォーマンス差があるそうです。

KSP 化を進める際は、 KSP に対応していないライブラリが存在することに注意が必要です。
そのようなライブラリを使用している場合は、 kapt と KSP を併用する必要がありますが、ビルド時間が増加する可能性があるため注意が必要です。

おわりに

Kotlin Fest 2024 まで、あと 2 日! https://www.kotlinfest.dev/

株式会社エブリー は、ひよこスポンサー として Kotlin Fest 2024 に参加します。 ぜひ、ブースでお会いしましょう!