【Qtデバイスの高速起動】Part3: システムイメージの最適化

この記事は The Qt BlogFast-Booting Qt Devices, Part 3: Optimizing System Image を翻訳したものです。
執筆: Risto Avila, 2016年5月25日

高速起動に関するブログ記事の3つ目です。最初の記事 ではクラスタのデモ が 1.56 秒で起動し、2つ目の記事
では Qt アプリケーションの最適化 について紹介しました。今回はブートローダーと NNXP i.MX6 SABRE ボード用の Linux カーネルの最適化に専念しましょう。

起動時間の高速化に着手する前に測った起動時間は 22.8 秒でした。その後ゴールを2秒以内に定めました。これに向けて様々な作業を行います。最初は一番わかりやすい rootfs からはじめました。既存の rootfs は今回のデモには必要のないものが数多く含まれていました。これにより、元々 500 MB だったサイズが 24 MB まで減らすことができました。buildroot を用いてターゲットデバイス向けの最小限の rootfs とクロスコンパイル用のツールチェインを用意しました。

この小さな rootfs で測定した起動時間は 15.6 秒でした。この中でカーネルの起動が 6 秒を占め、残りは U-Boot と最適化をしていないアプリケーションになります。というわけで、次はカーネルです。既にアプリケーションに必要な機能はすべて分かっているため、不必要なものの大部分を取り除くことでカーネルのサイズを 5.5 MB から 1.6 MB に減らすことに成功しました。これにより起動時間は 9.26 秒、カーネルの部分は 1.9 秒まで短縮することができました。

この時点では U-Boot は未着手でした。デフォルトの1秒の待機時間とカーネルの整合性チェックが有効になったままでした。というわけで、次は U-Boot の番です。この内部には「secondary program loader」と呼ばれる別の U-Boot や特別な設定のカーネルを起動するためのフレームワークが存在します。SPL モードを有効にして、カーネルにコマンドライン引数を追加し、デバイスツリーも追加しました。デバイスツリーは 47 KB あったものを 14 KB に減らし、コンソールを無効にしました。この効果で 3.42 秒まで起動時間が減りました。カーネルは 0.61 秒、残りが U-Boot とアプリケーションになります。

というわけで、下まわりのシステム(ブートローダー+カーネル)はまともな時間で起動するようになったため、アプリケーションの最適化を行いました。前回の記事 のとおり、計器類の外枠のみをなによりも早く表示し、アニメーションとともに残りのゲージや 3D の車のモデルを表示するような最適化を行いました。それでも起動時間は2秒の目標には達しなかったため、システムの部分についてさらに調査を行いました。クラス4のSDカードを使っていたため、それをクラス10のものに変えました。

共有ライブラリ形式で使用していた Qt のライブラリを静的リンクに代え、デモアプリをその上でビルドしなおしました。これによって共有ライブラリを rootfs から除くことが可能になりました。共有ライブラリの利用に必要な起動時のシンボルの解決が静的リンクでは必要なくなります。静的リンクによりアプリケーションを1つの 19 MB のバイナリにすることができました。これには必要なすべてのアセット(3D モデル、画像、フォント)と、Qt のライブラリが含まれています。それから、Qt のビルド時に必要な最適化のフラグを指定し忘れていたため、サイズの最適化の追加と fpic の無効化を行い、最終的に 15 MB まで減らすことに成功しました。また、rootfs を eMMC に置くと SD カードよりも起動が速いことに気づきました。

しかし、ブートローダーとカーネルのイメージをSDカードに置く方が両方を eMMC 上に置くよりも速かったので、ブートローダーとカーネルは SD カードから読み込み、rootfs を eMMC から読むという謎の組み合わせに落ち着きました。カーネルは gzip でパッケージングされています。UPX、LZO、LZ4 を試した結果、一番速かった LZO を採用するようにしました。ハードウェアによっては別の形式や、パッケージングをやめるという選択肢もありうるでしょう。このパッケージングの変更と、シリアルコンソールの無効化により、カーネルイメージのサイズは 1.3 MB まで小さくなり、起動時間も 1.94 秒まで短縮できました。

製品開発の際には、さらにメモリの設定など最適化の余地がまだあるでしょう。U-Boot にもカーネルイメージを SD カードから読む方が eMMC より速い件に関して調査の余地が残っています。一般的に、起動時間の短縮が必要な場合には、ハードウェアもそれに応じて設計されている必要があります。CPU から U-Boot とカーネルを高速に読めるような専用領域を用意し、rootfs はそれよりは少し遅い eMMC から読み込むような手が考えられます。

目標の2秒以内はすでに達成できましたが、さらに高速化できないか色々試してみました。カーネルからネットワークスタックを除いたところデバイスツリー込みで 1.2 MB まで減らすことができました。Vivante ドライバがモジュールとして提供されていたため静的な rootfs を作成することができなかったため、prelink を rootfs に対して実行することにしました。U-Boot の SPL の部分をさらに減らし、ブートローダは 31KB から 23 KB に小さくなりました。これらの作業の結果、システムが 1.56 秒で起動するところまで持っていくことに成功しました。

ここまでのまとめとして、起動時間の削減をグラフ化しました。

chart2

最後に起動時間に影響する要素としてはハードウェアの選択が残っています。これは同じ CPU を積んでいてもボードの起動時間が異なることがあるからです。時間を見つけて試してみたいと思います。

すべきこと:

  • どこに時間がかかっているのかを測定して分析する
  • 目標をなるべく早い段階で定める
  • 開発の初期段階でゴールを達成し、それをキープする
  • ソフトウェアの設計時に起動時間も考慮する
  • 簡単なところから最適化をはじめ、段階的に難しいところに進んでいく
  • 静的リンクが速い場合はそちらを利用する
  • ハードウェアの制限を把握し、可能であればハードウェアも最適化する

すべきではないこと:

  • ハードウェアの選択時に過大評価をしない。i.MX28 は iPad のようなパフォーマンスでは動作しません。
  • ソフトウェアの構成を複雑にしない。シンプルではあればあるほど速くなります。
  • 不必要なもののロードを避ける。あらかじめ用意されたイメージは様々なユースケースに対応するために機能がたくさん詰め込まれているため、最適化が必要となります。
  • プロジェクトの最終段階まで最適化をしない。
  • 最後の数ミリ秒の最適化のコストを低く見積り過ぎない。

以上で、起動時間の高速化に関する記事は終わりです。この3つの記事で、Qt が要求に十分応えうるということを示せたと思っています。業界の要求を満たすレベルまで Qt ベースのデバイスの起動を高速化することは可能です。何をしているかを理解すると実際にはとても簡単ではあるのですが、すべてを解決する1つの魔法は存在せず、美しいソフトウェアのアーキテクチャや Qt Quick の様々な工夫や最適なハードウェアの選定やシステムイメージの様々な最適化など、複数の最適化の組み合わせによって実現する必要があります。最後まで読んでくれてありがとうございました!


Blog Topics:

Comments