レポート

Qt 6に向けた議論が深まる ―「Qt Contributors' Summit 2018」参加レポート

この記事を読むのに必要な時間:およそ 4.5 分

Text in Qt

テキスト表示はUIで多用される機能ですが,しばしばパフォーマンスのボトルネックにもなります。このセッションではQt 5,6でのテキスト機能のパフォーマンス改善について議論になりました。

まずは従来のQtで実施されてきたテキスト描画の最適化手法についての紹介から始まりました。いくつかの実装がこれまでありましたが,どれも方向性としてはおなじで,文字列の整形処理(Shaping)を削減することです。整形処理とは文字のサイズを計算し,カーニングやリガチャ(合字)などを行って,文字を正しく美しく描画できるようにすることです。

QPainter::drawText() では必ず整形処理が実行されるため,テキスト描画のコストが最も重くなります。Qt 4ではQStaticTextクラスが導入されて,整形処理の結果をキャッシュすることが可能となり,Qt Quick 1のパフォーマンス改善に使用されました。Qt Quick 2ではQGlyphRunクラスでキャッシュするようになっています。Qt 5.10ではQFont::PreferNoShapingが導入され,ラテン系文字などで整形を簡略化してパフォーマンスの改善を図ることもできるようになりました。

こういった工夫は実施されていますが,テキストの一部だけが変化するような場合でも全体を整形し直したり,整形以外にもQt Quick 2でフォントのグリフ情報として使用しているディスタンスフィールドデータの生成や,そのGPUへのアップロードに時間がかかるなどの課題があります。

このセッションではディスタンスフィールドデータのファイルキャッシュによる起動時間の高速化やそのGPUへのアップロードの非同期化,引数でその一部が変化するような文字列で変化しない場所の整形結果をキャッシュするFormattedTextが提案されたりしました。

FormattedText

FormattedText

FormattedTextは個人的には面白い提案でしたが,これまではQString::arg()で指定される引数を,子オブジェクトのArgument系クラスで指定する方式の評判が良くなかったようでした。QMLのJavaScriptのarg()ではQString::arg()に比べて幅やフォーマットの指定ができないなどの制限があり,それらの解消にも使えるのではないかという個人的な思いもあったため,採用しない方向に向かいそうなのは残念です。

このセッションで提案された方法はキャッシングや別スレッドの利用による非同期化などのこれまでに実施されてきた手法の延長線上にあるもののため,実装的には大変そうですが理解しやすいものでした。

Qt + unique_ptr

30分のショートセッションでしたが,個人的に一番興味深かったのがこのセッションです。

Qtに限らずフレームワークでは 所有権(ownership)という考え方がよく用いられます。Qtの場合はオブジェクトの親子関係がその代表的な例です。オブジェクトが親子関係を持つ場合には,子オブジェクトの所有権は親オブジェクトが持ち,親オブジェクトが破棄される際に子オブジェクトも自動的に破棄されます。所有権を導入することでオブジェクトの管理が容易になりますが,フレームワークの利用者はどのAPIが所有権の変更を伴うのかを理解しておく必要があります。

親子関係のように使用頻度も多くわかりやすい場合には問題はないのですが,Model/ViewやQActionなどで複数のオブジェクトが1つのオブジェクトを参照する場合のように,所有権が移動しない場合を正しく認識していないと,メモリリークの原因となります。そのため,C++11で導入されたスマートポインタの1つであるstd::unique_ptrを用いて所有権をコード上で明記しようというのがこのセッションでの提案になります。

たとえば,従来

QWidget widget;
QLabel *label = new QLabel(&widget);

としていたようなコードは

QWidget widget;
QLabel *label = widget.makeChild<QLabel>();

と書いたり,

QWidget widget;
auto label = std::make_unique<QLabel>();
widget.adoptChild(std::move(label));

と書くようになり,コンストラクタでの親オブジェクトの指定やQObject::setParent()は非推奨のAPIとなります。

上記の例では記述量が増えただけでメリットはほとんどありませんが,たとえばQWidget::addAction(QAction *)のような生のポインタを受け取るメソッドでは所有権が移動しないため,呼び出し元に所有権が残ることが読み取れます。逆にQLayout::addWidget(std::unique_ptr)のようなメソッドでは,呼び出しを通じて所有権の移動が発生することがわかります。

非常に面白い提案で, std::unique_ptr を用いることでコード上で所有権が明確になり,プログラムの安全性を高めることが可能になります。

しかし,問題もあります。メジャーバージョンアップ時もソースコード互換性をできるだけ維持するという方針があるため,std::unique_ptrを使用しない従来のAPIを(非推奨にできても)廃止できません。std::unique_ptrを使用する場合にはコードの記述量も増加するため,より安全な仕組みとして用意したものがQtの利用者には使ってもらえない可能性があります。

また多くの場合,所有権を持つオブジェクト以外のオブジェクトからも被所有オブジェクトへのアクセスが発生します。このために生のポインタを保存したりしてしまってはせっかくAPIで担保しようとした安全性を損ねる原因ともなりえます。

このように課題は残るものの,コード上で所有権を明確化すること自体は有用な提案です。LarsやQt CoreのメンテナであるThiago Macieiraなどの主要な開発者が興味を持っていました。更なる研究が必要だと言うことで,上述の課題があるため現状のコンセプトをそのまま使用する形にはならないと思いますが,Qt 6ではこの提案をベースとした改良がくわえられるかもしれません。

Final session: Conclusions

サミットの最後はまとめのセッションとなります。各セッションの概要をそれぞれが説明していきます。

まとめ

昨年のQtCS 2017から具体化しはじめたQt 6ですが,今年はその機能的な部分以外のリポジトリや開発プロセス,タイムラインなども議論され,いよいよ開発に向けて本格化が始まった形となりました。まだまだ実際にQt 6向けリポジトリが準備されてるのは先となりますが,調査や設計などは既に始まっており,今回のサミットでは議論できなかった項目についてもJIRA上にタスクが作成されたりしています。ぜひ,Qt 6にむけてそれらのタスクを確認したり,議論や開発に参加してみてください。

著者プロフィール

朝木卓見(あさきたくみ)

1996年からQtを使い始める。

2006年,Qtの開発元であったTrollTech ASAにてQtのコンサルティングやサポートに従事。

2008年,TrollTech買収によりNokiaに移籍し,Qtの啓蒙活動なども行う。

現在は株式会社SRAでQtコンサルタントとして活動中。

Officially Certified Qt Specialist

コメント

コメントの記入