コンパイルの反復速度を高めるHotswap(ホットスワップ)とは?ビルドツールBuckの便利な機能

ソースを変更するたびに、コンパイルして確認する作業は手間がかかります。そのプロセスをもっと簡易的に。そんな思いで開発され、オープンソース化されたのが、Facebookのコンパイル反復の速度を上げるビルドツール「Buck(バック)」です。

開発効率アップの決め手となるのは、編集・コンパイル実行プロセスにおけるのインクリメンタルな性質を解消することです。Buckは加えた変更のローカルなモジュールのセットだけを再コンパイルします。デバイスを圧迫するようなAPKをすべて生成する必要がありません。

Buckの現在の実行方法は、テストアプリケーション(以下アプリ)を完全にシャットダウンし、変更されたコードで再起動させることです。アプリを再起動したら一度リセットし、編集画面に戻るか、自分が修正しているバグを再現する必要があります。アプリの反応が悪くなると、当然開発サイクルも遅くなってしまいます。Buckは、状態をリセットするサポートをしていますが、ビジュアル管理フレームワークまたはフラグメントベースを使用して単一アクティビティのみを行うアプリはサポートできません。

電源を完全に切って、ハードウェアが初期化された状態からの再起動(コールドスタート)時間や、ログイン時のような追加の状態を設定するしかありません。

Facebookはさらに「HotSwap(ホットスワップ)」という形でBuckにホットコードのリロードを行えるようにしました。この記事では、HotSwapの使い方や既存のサービスと比較したときのパフォーマンスなどを書いていきます。

HotSwapの使い方

タスクを実行するために、HotSwapは2つのソリューション、すなわちBuckの既存のExopackageのアンドロイド開発のサポートと、Java ClassLoaderを組み合わせています。

ExopackageでAPKを生成


Exopackageは、Buckのインクリメンタルインストールへのアプローチです。最初にアプリがデバイスにインストールされると、最小限のコードセットを含むAPKが生成されます。

Buckは残りのコードをデバイスの一時ディレクトリにコピーし、アプリは起動後にそのコードを読み込みます。変更されたコードだけを一時ディレクトリにコピーする必要がありますが、パッケージマネージャを起動する必要がなく、デバイスに新しいAPKを送る必要もないため、インストールが大幅に高速化されます。

ClassLoaderでコードを読み込む

コードがデバイス上に載ったら、ClassLoaderでコードを読み込みます。

デフォルト設定では、ClassLoaderは階層内でつながっています。新規のインスタンス作成や静的メソッド呼び出しなどのクラス参照をするたびに、最初のクラスは独自のローダに目的のクラスの定義を問い合わせます。これにより、loadClassが呼び出されるのです。この呼び出しは、フォールバック検索として実装されています。

以下のステップを見ていきましょう。

  1. ローダは、クラスのキャッシュを確認し、以前ロードされた定義があるかどうかを調べます。
  2. ClassLoaderはその親に対してクラスを要求します。これにより、javaやアンドロイドでクラスの競合する定義をロードできなくなります。
  3. これらのチェックのどちらも定義を生成しない場合、ローダーはクラスのdexファイルをチェックして、ディスクからロードします。
  4. 上記のすべての手順が失敗した場合、ローダーには”ClassNotFoundException”と表示されます。

キャッシュからエントリを削除し、ディスクから新しいエントリを読み込むようにClassLoaderに指示できることが理想的なのですが、実際のところ、個々のエントリをキャッシュから一つずつ削除していくより簡単な方法はありません。

HotSwapは、定義が変更されたときにディスクから一度削除され、再投入ができるようなデリゲートClassLoaderを階層に入れ込みます。既存のBuckでも、すでに独自のClassLoaderが入っており、exopackageディレクトリからクラス定義をロードするため、HotSwapの場合は単にデリゲートローダーを追加します。

その後、HotSwapのエンドツーエンドフローは以下のようになります。

  1. Buckが変更したコードを徐々にコンパイルします。
  2. Buckのexopackageサポートで、変更されたコードが一時ディレクトリにインストールされます。
  3. デリゲートClassLoaderがスローされ、新たなClassLoaderが作成されます。
  4. 変更されたクラスをロードする呼び出しでは、新しく変更されたコードが表示されます。

パフォーマンス評価

HotSwapがもたらすワークフローの改善についての基準を策定するため、Facebook開発チームはサンプルアプリケーションに些細なバグをあえて導入しました。そしてコード内のバグを修正し、コンパイルが完了してからバグ修正が確認できるまでの時間を測定したようです。

HotSwapでは、既存のBuckとは違って再起動とそのナビゲーションの手順が省略され、バグ修正の検証に要した時間が90%短縮されました。編集コンパイルの実行が、HotSwapだけで確実に速まったのです。

その秘密は何か。HotSwapは、実行時に新しいクラスを定義するのではなく、インクリメンタルインストールを拡張して、コードの再読み込みを可能にしています。

Buckのインクリメンタルインストールサポートを利用すると、HotSwap経由でロードされた変更は、アプリの再起動中に永続的に保持されます。つまり、再起動する必要がある場合、コンパイルされた定義とデバイス上のメモリで実行されている内容との間に矛盾は起こりません。

HotSwapでは、より包括的な編集も可能です。変更されたクラス定義を新しく読み込むため、どんなコードの一部でも編集することができます。これには、新しいメソッドやフィールドも含まれます。

上記のアプローチの欠点といえば、ほとんどの場合、アクティビティの再起動が必要であることです。アクティビティまたはフラグメントコードの変更に伴って、新しいインスタンスを作成しなくてはなりません。将来、メソッド定義のスワップを追加することもできますが、アクティビティを再起動してもエクスペリエンスが大幅に低下することはないのでご安心ください。

HotSwapはアプリ内のコードのリロードに特化しているため、リソースの変更は現時点では推奨されていません。しかし今後、カスタムアセットマネージャーなど、リソース定義の変更に対するサポートの追加も考慮しています。

HotSwapはまだ初期段階ですが、これから積極的に使っていき、ユースケースを増やすようです。今後の展開が楽しみですね。

(翻訳:Kerala)

SHARE

RELATED

  • お問い合わせ
  • お問い合わせ
  • お問い合わせ