Oct 06, 2010

Firefox 4: jar jar jar

この間悩まされたばかりのomnijarの話である、Taras’ Blog » Blog Archive » Firefox 4: jar jar jarの勝手翻訳です。分からない所が多かったので創作的意訳が多めですが気にしません。


ファイルを開く処理というものは、システムコールのための小さなオーバーヘッドや、ディスクからのデータの先読みによる大きなオーバーヘッドがある、比較的重たい処理です。データの物理的な配置の状態やディスクの種類によっては、この処理は今時の高速なCPUに対して、複数の異なるファイルそれぞれの全ての断片を先読みするためにディスク上をヘッドが激しく行き来する間、長々と無駄に暇をもてあまさせる事にもなり得ます。

最適化その1:裸のファイルをより少なくする

約2年前、私(訳注:元エントリの著者のTaras Glek)はディスク上にある裸のファイルを収集してjarファイルに詰め込む作業を始めました(例:bug 508421)。また、コードのクリーンアップやmmapへの移行などを通じて、私達はjarファイルの読み込み処理の効率も可能な限り改善しました。そして最終的には、通常の起動時にディスクから読み込まれていた、アプリケーションを構成するデータファイルの全てが、jarファイルの中に格納されるようになりました。しかし残念ながら、私達は4つのjarファイル(toolkit、chrome、そして2つのロケール用jarファイル)に構成ファイルをまとめる所までしか至れませんでした−−何ともマヌケな事ですけれども。またXPCOMの制限により、多数の裸のファイルがまだ、Firefoxのアップデートや拡張機能のインストールの度にディスクから読み込まれる状態となっていました。

最適化その2:全てを統べる1つのjarファイル

最近、Michael Wuがomnijarという滅茶苦茶にヤバい物を解き放ち非常に影響範囲の大きな変更を導入しました。これはAndroid用パッケージ作成での必要に迫られて行われた非常に大きな取り組みです。今や、アプリケーション起動時に必要なデータは常に単一のファイルから読み込まれるようになりました。これはデータの位置の集約化、ファイル走査の手間の短縮、そして待ち時間の短縮に繋がります。複数のファイルを1つに小さくまとめる事の1つの利点としては、OSがディスクからデータを読み込む際に、大抵の場合はアプリケーションが要求したよりも大きな単位で、投機的にディスクからデータを読み込む事も挙げられます。これにより、隣接するファイルの読み込みがより自由に行えるようになります。残念ながら、Firefoxを実際に実行せずにファイルアクセスの順番を予測するための良い方法がなかったため、ここにはさらなる改善の余地があります。

最適化その3:jarファイル内でのファイル配置の最適化

さて、今や全てのデータが1つのファイルの中に置かれましたので、論理的な次のステップは、それらをより賢く格納する事でした。それを行うための唯一の方法は、Firefoxの起動時の処理を分析して、jarファイル内のファイル配置をその結果に基づいて並べ替える事です。残念な事に、jarファイル内の全ての項目を連続的に配置するにあたって、私達はまだ次善の策を取っています。これは、ZIP(jarファイルの実態はZIPファイルです)のインデックスが伝統的にファイルの終端に配置されている事に依っています。Wikipediaの記事にこれを図示した物があります。

先読みの利点を最大限に活かし、ディスクの走査を最小限にするためには、格納されたファイルのインデックスがZIPファイルの先頭に置かれているのが望ましいです。そのため、私は私達のZIPファイル内のデータの配置を、

<項目1><項目2>…<項目N><インデックス><インデックスの終端>

から

<最初に読み込まれる、最後の項目の位置のオフセット値><インデックス><インデックスの終端><項目1><項目2>…<項目N><インデックスの終端>

に変更しました。

ここで私がやったのは、<インデックスの終端>のオフセット値を常に4になるようにした(どうでもいい事に拘るZIPアーカイバはインデックスのオフセットがNULLだとフリーズしてしまう事があるので、これは0にすることはできません)だけです。その際、インデックスは常に前のインデックスの終端に続けて配置されなければならないという仕様を満たすために、私は最初の物と全く同じ2つ目の<インデックスの終端>を加えました。また、私は、過度に用心深いZIPアーカイバによって強制された、どれだけのデータを最初に先読みしておく事ができるかを示す数値を格納するための追加の余白も利用する事にしました。

これによって、最適化されていないomnijarに比べてディスクI/Oの2〜3倍の削減を実現しました。これは裸のファイルをomnijarにすることで20〜100倍以上の高速化が実現できた事の最大の要因です。

私がZIPの仕様を読んでガッカリしたのは、ZIPアーカイバの中にはZIPファイルが仕様が許容しているよりもずっと厳格な形式になっている事を期待している物があるということです。以前のバージョンのFirefoxや、Microsoft WindowsのZIPサポート(訳注:圧縮フォルダ)、WinRAR、UNIXのZIPアーカイバなどは、私の最適化されたjarファイルを受け付けてくれますが、7-Zipや壊れたアンチウィルスソフト(スキャン対象を無闇矢鱈に限定する事はセキュリティ上危険です)はこれらを開く事ができません

豆知識:これは、受け付けるZIPファイルの内容を選り好みするソフトウェアのせいで困らされた最初のケースではありません。例えば、Android標準のAPK読み込み処理は、Android用のパッケージが0バイトの大きさの項目を含んだZIPファイルである場合に、いちいちしつこくそれを警告してきます。これは、APK形式のファイルをWindowsにおける自己解凍形式のEXEファイルのように使う事ができないということです。Michael Wuはこの問題を解決するための独自の読み込み用ライブラリを書いている所です。

最適化その4:さらなるomnijar

omnijarは十分に素晴らしいとはまだ言えないということで、Michael Wuはさらに先に進んで拡張機能をomnijar化しました(訳注:アドオンがXPIのままで認識されるようになった事を述べたエントリ)。ほとんどの拡張機能はXPIから裸のファイルに展開される必要が無くなる事でしょう。これは、拡張機能の作者が上記のような最適化されたjar形式を、Firefoxの起動を高速化するために利用できるという事を意味します。

その他のjarファイルの最適化

起動処理の高速化用のキャッシュをjarに切り替える事によって、私達は最初の起動処理をさらに最適化できるようになることでしょう。私が最適化されたjarファイルに加えた先読み用の情報を実際に利用する事によって、jarファイルのI/Oを半減できる可能性もあります。

元記事に寄せられたコメント

Benによるコメント(2010-09-23 05:32pm)

2点だけ:

1つ目。あなたがどのように言おうと、これらの「最適化された」jarファイルはもはやZIPファイルではありません。仕様は非常に明確で、インデックスはファイルの末尾になくてはなりません。

あなたがやった事に間違った点はありませんが、しかし、それらのファイルをZIPであるかのように偽ろうとした上で、ZIPを扱うアプリケーションがそれらに対して文句を言わないようになる事を望むというのは、良い事ではありません。それよりもあなたは、最適化されたファイルの拡張子を変更することにして、最適化とその解除を手動で行うためのツールを提供した方がよいでしょう。

私は、例えば7-Zipは仕様に対して非常に厳格に設計されているがために、これらのファイル形式をサポートする事はあり得ないだろうと確信しています。

2つ目。私はFirefoxのコールドスタート(訳注:キャッシュ等にファイルが読み込まれていない状態からの起動)が(特に他のブラウザと比べて)遅いということを体感して、少し調べてみる事にしました。新規作成したプロファイルであってもまだ起動は遅かったです。私は、Firefoxは起動時に18個ものDLL(それらは全て、Firefox/Minefieldのインストール先ディレクトリに置かれています)を読み込んでいるという事に気がつきました。これは非常に良くない事で、ファイルの数の問題だけでなく、Windowsではセキュリティ対策ソフト(既定の状態であればWindows Defender、大抵の場合は何らかのアンチウィルスソフト)においての問題もあります。

私のマシン上では、ファイルのスキャンは1つあたりだいたい50ミリ秒を要していますが、この処理時間はファイルサイズには影響を受けません。Firefoxの場合、起動に数秒を要してしまいます。セキュリティ対策のソフトが大抵の場合は必要が生じた際の動的なスキャンの結果をキャッシュしていて、再起動されるか定義ファイルが更新されるまでの間は再スキャンが行われないために、この問題がウォームスタート(訳注:ファイルがキャッシュ等に読み込まれた状態からの起動)の場合には起こらないという事には注意して下さい。

いくつかの他のブラウザは、起動時にDLLを2つだけ読み込んでいて、他は必要が生じた時(動画の再生、WebGLを使う場合など)に動的に読み込んでいます。これはFirefoxの場合よりもずっといい具合に動作しているように思われます。

検索してみた所、私はLinuxについてのこの件に対するバグをいくつか見つけましたが、Windowsについてのバグは見つかりませんでした。それらが1つの大きなDLL(xul.dll)とそれ以外の小さなDLL(それらは無条件に読み込まれる)となっているので、全てを1つのファイルにまとめるのは理にかなっていると思います。

Taras Glekによるコメント(2010-09-24 09:06am)

Ben、あなたが注目した多数のDLLを提供している点についてはあなたの指摘は的を射ています。それについてのバグは https://bugzilla.mozilla.org/show_bug.cgi?id=561842 です。残念ながら、私達はこの作業をFirefox 4のリリースには間に合わせられませんので、後のリリース版で反映される事でしょう。

私はアンチウィルスソフトが起動時の処理に与える影響についても、より詳しい情報を得たいです。

エントリを編集します。

wikieditish message: Ready to edit this entry.











拡張機能