この記事は The Qt Blog の Fast-Booting Qt Devices, Part 2: Optimizing Qt Application を翻訳したものです。
執筆: Risto Avila, 2016年4月27日
先日の記事 では i.MX6 ボード上で2秒で起動する高速起動デモの紹介をしました。今回の記事では Qt QML のクラスタアプリケーションをどのように最適化したのかをカバーしたいと思います。
Qt World Summit 2015 で展示した当初のデモは PC 環境で設計され、起動時間についてはそもそも考慮されていませんでした。Loader を利用して UI のパーツを非同期にロードするような設計にはなっていましたが、起動シーケンスについてはまったく間が得られていませんでした。最適化の初めの一歩として、ユースケースについて考える必要がありました。「はじめにユーザーに見てもらいたいものはなにか?」を検討した結果、一番初めは計器類の外枠を表示し、その後で画面に表示する他のものをロードするということにしました。
以下の画像で赤いオーバーレイの部分がアプリケーションの起動直後にユーザーが目にするエリアになります。

アプリケーションのソースコードを見てみると、ダッシュボードは複数のマスク画像で、その中のいくつかは画面と同じ大きさでした。そのためこれをすべて結合し、1つのフルスクリーンの画像にし、UI の最前面に表示させるようにしました。

アプリケーションの起動を一秒でも速くするために、アプリケーションの内部構造も色々見直しました。ダッシュボードのフレームを1つの QML ファイルにまとめ、一番最初にロードするようにしました。この外枠がロードされ描画されたあとで、UI の他の部分をロードするためのローダーを有効にするようにしました。

実際にどういった処理に時間がかかっているかを調べるために、Qt Creator の QML プロファイラも活用しました。元々このデモではデスクトップアプリ向けに開発されていた Qt Quick Controls を利用していて、これによって余計に時間がかかっていた部分がありました。(余談ですが、Qt Quick Controls は組み込み向けに 設計が見直されたものが Qt 5.7 に追加 されます。)これを解決するためにゲージの部分を画像ファイルとアニメーションが必要なゲージの一部の色を変更するようなフラグメントシェーダーで置き換えました。
最後に、フリップアニメーションをゲージに対して追加し、車にフェードインの効果を追加し、起動の流れをより自然なものにしました。
https://youtu.be/Nm_LT1prTR8
これらの最適化の結果、ターゲットデバイス上で、Qt アプリケーションの最初のフレームが 300 ミリ秒で表示できるようになりました。
アプリケーション最適化時にすべきこと、避けるべきこと
というわけで、自分たちの経験を元に Qt Quick アプリケーションの最適化についてまとめてみました。これを試しても目標が達成できない場合は、弊社、もしくは弊社のパートナー まで連絡いただければ喜んでお手伝いいたします。
すべきこと:
- アプリケーションの起動の高速化を初めから見直す。一番初めにユーザーに表示するものについて考える。
- UI のアニメーションを追加し、裏でロードを並行して行う。
- チェーンロードを行う。CPU が搭載しているコア数に合わせて同時に動作するローダーの数を最適化する。(例えば、2コアの場合は2つのローダーを並行して使いまわす)
- 一番初めのローダーは非同期にしない。他のローダーは非同期で実行する。
- 必要な時点でロードされる QML プラグインを作成する。
- バックエンドサービスへの接続は必要な時のみに限る。
- QML プラグインにおいて重要ではないサービスは適宜スタート、ストップできるようにする。
- png / jpg 画像を最適化する。
- 頂点数を減らしたり見えない部分を削除することで 3D のモデルを軽量化する。
- glTF で 3D モデルのロードを最適化する。
- Qt Quick Controls 2.0 を使用する。これは組み込み用途に設計されており、Qt Quick Controls 1.0 に比べてコントロールの生成時間が大幅に改善されている。
- clip と opacity の利用を最小限に留める。
- GPU の上限を把握し、UI の設計時にそれを考慮する。
- Qt Quick Compiler を用いて QML ファイルをコンパイルする。
- 静的リンクが利用可能か調査する。
- シグナルハンドラでの処理をの代わりに宣言的なプロパティバインディングを使うように努める。
- プロパティバインディングはシンプルにする。QML のコード自体もシンプルで、ワクワクして、読みやすいようにしましょう。パフォーマンスは自然とよくなるはずです。
- 複数のプラットフォームや見た目が必要な場合は動的なコンポーネントのロードではなくファイルセレクタを利用しよう。シンプルな QML のコードに関しては「複製」を恐れず、ファイルセレクタで専用のバージョンをロードしよう。
避けるべきこと:
- QML であれこれやりすぎない。QML で可能だということと、QML ですべきことは別。
- main.cpp ですべての初期化処理を行うこと。
- 必要となるすべてのインターフェースを提供する巨大なシングルトンの作成。
- ListView に複雑な delegate を利用すること。
- 組み込み向けで Qt Quick Controls 1.0 を利用すること。
- clip は可能な限り避ける(98% のケースではこれは可能なはず)
- Loade の使いすぎに注意。Loader は「ページ」のような比較的大きな要素の遅延ロードに便利だが、シンプルなもののロードの場合はオーバーヘッドの方が大きい可能性が。すべてのものを高速化するための黒魔法ではない。QML の別のコンテキストを提供する要素にすぎない。
- 再利用のしすぎ。再利用にはバインディングや複雑性の増加、パフォーマンスの低下につながりやすい。
次のパートではオペレーティングシステムまわりの起動時間の最適化似ついてです。お楽しみに!