本稿は「Optimizing your QML application for compilation to C++」の抄訳です。
この連載では、QMLスクリプトコンパイラであるqmlscを使用してQMLアプリケーションを最適化する方法について、貴重な洞察をお伝えしていきます。これまでの記事とは異なり、具体的なコード例に焦点を当て、パフォーマンスを最適化するための修正方法について具体的に提案します。抽象的なアーキテクチャやハイレベルな概念に興味がある方は、ぜひ以前の記事をご覧ください[1] [2] [3]。それではさっそく、実践的な例で手を動かしてみましょう。
このシリーズの今後のテーマは以下の通りです:
Qt の最新機能の探求を続けるうちに、手法のリストが拡大する可能性があることは注目に値します。これらの機能の中には、まだ一般に公開されていないものもあるかもしれません。しかし、これらの手法を実装するために必要なQtの最小バージョンは常に明記しておきます。
では、実用的な面に飛び込んでみましょう。まず始めに、作業に適したソフトを探す必要があります。
Qt CreatorのQMLプロファイラが登場してからしばらく経ちますが、忘れてしまった方もいらっしゃるかもしれません。しかし、アプリケーションの性能を測定し、先行コンパイルによる高速化を正確に測定することができる信頼性の高いツールであることに変わりはありません。とはいえ、やや古いQMLアプリケーションであるため、QML Profiler GUIは現在、C++にうまくコンパイルすることができません。今回は、この問題を解決し、QMLプロファイラを使って、バインディング評価の性能に与える変更の影響を観察してみます。
ここでは、Qt Creatorを改良の対象としていますが、ここで紹介する手法は、どのようなアプリケーションにも適用可能です。I/OやC++で実装された内部データ変換など、ワークロードによって性能が制限されるため、QMLプロファイラで信じられないほどの性能向上を達成することはできませんが、QMLコードに加えた変更の性能への影響を直接観察する方法を紹介するつもりです。
まず、環境を整える必要があります。以下は、Qt Creatorをビルドし、QMLプロファイラを利用するための簡潔なガイドです。
最初のステップは簡単です。QtとQt Quick Compiler ExtensionsをQtメンテナンスツールからインストールし、Qt Creatorのgitリポジトリ(サブモジュールも含む)をクローンします。
Qt互換コンパイルの問題を避けるためにmasterブランチをチェックアウトすることができますが、これから行う変更のいくつかはすでにmasterに統合されていることに留意してください。6.0などの古いブランチを見れば、オリジナルのコードを見ることができます。
次に、Qt CreatorでCMakeLists.txtを開いてください。上記ステップでインストールした Qt バージョンのキットの release with debug info 構成を選択します。qml2puppetはQtの新しいバージョンごとに調整する必要があるため、ビルドしようとしないほうがよいでしょう。ここでやろうとしていることでは必要ありません。src/tools/CMakeLists.txtで、次の行を削除してください。
add_subdirectory(qml2puppet)
ここで、QMLのデバッグとプロファイリングを有効にする必要があります。Qt Creator が提供するデフォルトのアプリケーションテンプレートは、debugおよび release with debug info の構成に対して自動的にこれを行いますが、Qt Creator のビルドシステムは、このようなテンプレートから作成されるわけではありません。QML デバッグとプロファイリングを有効にするには、プロジェクトモードに切り替えて、「QML debugging and profiling」ドロップダウンを「Enable」に設定します。
次にビルドします。これによって、すでにいくつかの有用な警告が表示されます。実際、かなりの数が表示されます。例えば、次のようなものです:
Warning: TimeMarks.qml:93:32: Unqualified access
anchors.right: scaleArea.right
これは、まさに私たちが望んでいることです。詳しくは後述しますが、まずはQMLプロファイラのテスト実行を行い、サンプルトレースを保存してみましょう。これは他のトレースと比較するためのものではなく、最適化するコードのトリガーとしてQMLプロファイラに読み戻すためのテストデータです。
Qt Creatorで、Debugビューを選択し、ドロップダウンメニューからQML Profilerを選択します。
ドロップダウン・メニューの隣にあるツールバーの緑色の三角形の「Start」ボタンを押します。これにより、Qt Creatorの別のインスタンスが起動し、QMLプロファイラがトレースを記録します。ツールバーの "Elapsed "タイムが刻々と変化し、すべてのビューに "Profiling Application "というラベルが表示されることに気づくでしょう。Qt Creatorの追加インスタンスを閉じます。その結果、タイムラインビューには、わずかなイベントしかない退屈なトレースが表示されることになります。
とりあえずこれで問題ありません。タイムラインビューで右クリックし、ドロップダウンメニューから「Save QML Trace」を選択します。トレースの保存場所とファイル名を選択します。後で再読み込みします。
色々なところでこういう警告が出ます:
Warning: ButtonsBar.qml:41:5: Could not compile function updateLockButton: Functions without type annotations won't be compiled
function updateLockButton(locked) {
ButtonsBar.qmlは、QML Profiler and Performance AnalyzerのTimelineビューの左上隅にあるボタン列のことです。このボタンにより、イベントのステップ実行やビューのズームが可能です。ここで訴えた関数は、「view event information on mouseover」ボタンがトグルされ、ホバー選択が有効または無効になったときに発動されます。どれくらいの時間がかかるか見てみましょう。
以前と同様にQt Creatorのプロファイリングを開始し、開いたQt Creatorの新しいインスタンスに切り替えてください。QMLプロファイラを開き、タイムライン上で右クリックし、私たちの例のトレースを「Load QML Trace」してください。そして、左上の「view event information on mouseover」ボタンを数回トグルします。
最後に、トレースされているQt Creatorのインスタンスを閉じ、トレースを記録しているQt Creatorのインスタンスに切り替えます。そこで、タイムライン上にいくつかのイベントが表示されます。
タイムラインの最後の部分が注目するところです。タイムラインは、下部のオーバービューを使ってスクロールすることができます。
そこに表示されるのは、これまでのアプリケーションとのインタラクションをそのまま表現したものです。ボタンを切り替えるマウスクリックは入力イベントとして記録され、そのうちのいくつかは、バインディングとJavaScriptの評価のカスケードを引き起こします。(注:イベントのカテゴリは、タイムラインビューの左側にあるラベルにドラッグ&ドロップすることで並び替えることができます)。
イベントをクリックすると、小さな情報ウィンドウでそのイベントに関する情報を見ることができます。そうすると、Qt Creator は対応するソースコードにジャンプすることもできます。このような単純なトレースでは、タイムラインのイベントを見るだけで、探している関数を簡単に見つけることができます。各入力イベントの一番下にあるJavaScript関数です。これをクリックすると、updateLockButton()の特定の呼び出しに関する情報が表示されます。統計ビューでは、同じタイプのすべてのイベント(この場合はupdateLockButton()のすべての呼び出し)の集計を見ることができます。すべてのビューの選択項目は同期されています。したがって、Statisticsビューに切り替えるだけで、正しい行に移動することができます。
私のパソコンでは、8回のトグル操作で累積61.4μs、1回の呼び出しで平均7.67μsの時間がかかりました。もちろん、これは統計的に有意な測定値ではありません。しかし、実証実験としては十分な結果です。
この数字は、このシリーズの次の投稿まで覚えておきましょう。さて、QMLプロファイラについて記憶を取り戻したところで、コードを修正してその効果を観察することにしましょう。次回は、型アノテーションがない関数の扱いについて紹介します。