Windows Vista SP1 でのファイル コピー機能強化の詳細

翻訳元: Inside Vista SP1 File Copy Improvements (英語)

Windows Vista SP1 リリースでは、アプリケーション互換性、デバイス サポート、電源管理、セキュリティと信頼性の各領域で、最初のリリースからの機能強化が多数盛り込まれています。機能強化の詳細な一覧は、こちら (英語) からダウンロードできるホワイトペーパー『Notable Changes in Windows Vista Service Pack 1』に記載されています。ファイル コピー機能の強化は同ホワイトペーパーで取り上げられているトピックの 1 つですが、Windows Vista SP1 では、同一ディスク上でのローカル コピー、Windows Vista 以外のシステムからのリモート コピー、Windows Vista SP1 システム間でのコピーを始めとするさまざまなシナリオでファイル コピーのパフォーマンスが向上しています。Windows XP から Windows Vista の最初のリリース、そして Windows Vista SP1 へというアップグレードのたびにファイル コピー エンジンに対して修正を加えていくことによって、こうしたパフォーマンスの向上を実現しています。ファイル コピーはユーザーが共通に行う操作です。そこで、今回は個々のコピー シナリオについて語るのではなく、コピー エンジンの改良の過程を追いながら、Windows Vista SP1 でのパフォーマンス向上の理由について詳しく説明します。

ファイル コピーとは、コピー元のファイルを開いてコピー先を指定した後、コピー元ファイルの読み取りとコピー先ファイルへの書き込みを実行するという処理であり、比較的単純な処理のように思われます。しかし実際には、コピーの処理状況の正確な表示、CPU やメモリの使用量、スルー プットなどのさまざまな要件がパフォーマンスに関与します。一般に、1 つの要件の最適化を図るとその他の要件が損なわれます。また、こうしたトレード オフの中から、望ましい妥協点を見出すのに役立つセマンティックな情報を認識することができません。たとえば、ユーザーがコピー元のファイルにアクセスするつもりがないことをコピー エンジンが認識できれば、そのファイルのデータをメモリにキャッシュせずに済ませることができます。他のアプリケーションによってすぐにファイルが使用されてしまうこと (ファイル サーバーの場合は、クライアント システム間でファイルが共有されてしまうこと) が分かっていれば、コピー先のシステムにデータをキャッシュできるはずです。

以前のバージョンの Windows でのファイル コピー

パフォーマンスでのトレード オフが存在し、かつ、利用できる情報に不足があることから、Windows のファイル コピー エンジンであらゆるシナリオをサポートするための取り組みが進められてきました。Windows Vista より前のバージョンの Windows では、コピー元とコピー先の双方のファイルをキャッシュ モードで開き、コピー元のファイルを 64 KB ずつ (ネットワークを介したコピーでは SMB 1.0 プロトコルの制限に準じて 60 KB ずつ) シーケンシャルに読み込み、そのデータをコピー先のファイルに書き込むという単純なアプローチをとっていました。メモリ マップ I/O、バッファ フラグのない I/O の場合とは異なり、キャッシュ I/O によるファイル アクセスでは、読み取りや書き込みが行われたデータは、少なくとも、メモリ マネージャーがメモリを他の用途 (他のファイルのデータのキャッシングなど) に使用すべきと判断するまで、メモリに格納されます。

コピー エンジンでは、Windows のキャッシュ マネージャーの判断に基づいて非同期的な先読みを実行します。バックグラウンドでコピー元ファイルの読み取りを行いながら、エクスプローラーが他のディスクやリモートのシステムにデータの書き込みを行います。また、キャッシュ マネージャーの遅延書き込みメカニズムによって、コピー元ファイルの内容を適切なタイミングでメモリから消去してディスクに書き戻します。これにより、必要に応じてメモリを素早く他の用途に使用することができるほか、ディスクやシステムに障害が発生した場合のデータ損失のリスクを最小限に抑えます。こうした処理のアルゴリズムは、次に示す Process Monitor のトレース画面で確認できます。これは、Windows XP 上で 256 KB のファイルを別のディレクトリにコピーする処理を示しています (わかりやすいように、データの読み取り/書き込み処理のみ表示しています)。

図 1

図 1

イベント 0 では、エクスプローラーによる初回のデータ読み取り処理が行われていますが、メモリ内にデータが存在しないためキャッシュ マネージャーが非キャッシュ I/O を実行しています。非キャッシュ I/O では、データをメモリにキャッシュすることなく直接ディスクで読み取り/書き込みを行います。イベント 1 では、ディスクからデータをフェッチしています。次に、そのスタック トレースを示します。

図 2

図 2

ここでは、フレーム 22 の BaseCopyStream 関数でエクスプローラーによる ReadFile の呼び出しが実行され、キャッシュ マネージャーがファイルのメモリ マップにアクセスして非キャッシュ読み取りを間接的に実行した結果、フレーム 8 でページ フォールトが発生しています。

エクスプローラーは順次アクセスでファイルを開いているようなので (上のトレースからは分かりません)、イベント 2 および 3 では、エクスプローラーが開いたファイルを、キャッシュ マネージャーの先読みスレッド (システム プロセスで実行) が読み込んでいます。イベント 2 のスタックの先読みスレッドを次に示します。

図 3

図 3

ここでは、エクスプローラーによる初回の読み取り処理によって実行された、コピー元ファイルをキャッシュを使用せずに先読みする処理が、冒頭でうまく機能していません。これによりディスクのヘッド シークが発生し、パフォーマンスが低下する可能性がありますが、その後、エクスプローラーがキャッシュ マネージャーによって既に読み込み済みのデータを取得できるようになり、読み込み処理をメモリから行えるようになった時点で、非キャッシュ I/O の実行が回避されています。 キャッシュ マネージャーは、ファイル コピー処理において、通常エクスプローラーよりも 128 KB 分データを先読みします。

イベント 4 では、エクスプローラーは最初の書き込みを実行し、その後、読み取りと書き込みを交互に行っています。トレースの最後では、キャッシュ マネージャーの遅延書き込みスレッドがシステム プロセスで実行され、コピー先ファイルのデータをキャッシュを使用せずにメモリからディスクに書き込んでいます。

Windows Vista でのファイル コピー機能の強化

Windows Vista の開発では、製品チームはファイル コピーの主要シナリオでの機能強化に取り組みました。ファイル コピー エンジンの実装に関する大きな課題の 1 つとして、大量のデータをコピーする場合に、コピー先のシステムで実行されるキャッシュ マネージャーの遅延書き込みスレッドの処理速度が、メモリへのデータの書き込み/キャッシュの速度に追いつかないという問題がありました。この結果、メモリが大量のデータで占有されて、本来必要なコードやデータを格納するメモリ領域がなくなり、コピーされたデータすべてがコピー先のシステムのメモリを介して処理されることになり、その速度はディスクの処理速度にまで低下していました。  

そのほかにも、リモート システムからローカル システムにコピーする際に、コピー元ファイルの読み取り時とコピー先ファイルへの書き込み時の 2 度にわたってファイルの内容がキャッシュされてしまうという問題がありました。この結果、再度アクセスする可能性の低いファイルによってクライアント システムのメモリが圧迫されるだけでなく、さらに、キャッシュ マネージャーによるコピー元/コピー先の両ファイル間のメモリ マップの管理という CPU のオーバーヘッドを招いていました。

比較的サイズの小さいファイルの読み取り/書き込みでは、SMB ファイル システム ドライバー (Windows のファイル共有プロトコルを実装するドライバー) が、WLAN を始めとする広帯域・高遅延のネットワークでデータ入出力のパイプラインを確立できないという問題が存在していました。そのため、コピーでは、ローカル システムがリモート システムから送られてくるデータを待機する都度、ネットワークでデータ フローが消費され、両システムが相手の確認応答と次のデータ ブロックの送受信を待機するために遅延が生じていました。

開発チームではこのような状況への対応策を広範に検討し、特定されたすべての問題を解決するために、非同期的で大規模な非キャッシュ I/O を実行できるようなコピー エンジンを実装することにしました。非キャッシュ I/O では、コピー元ファイルのデータによってローカル システムのメモリが消費されることはないため、メモリ上に既に存在する情報は保護されます。また、非同期的な大規模ファイルの I/O のサポートにより高遅延ネットワークにおけるデータのパイプライン処理が可能になります。キャッシュ マネージャー側では、メモリ マップの管理が不要になるため CPU 使用量が減少します。また、Windows Vista のキャッシュ マネージャーの大規模な I/O 処理における非効率性を考え、非キャッシュ I/O の採用に至っています。しかし、ここで、任意のサイズの I/O を実行できないという問題が生じました。コピー エンジンは書き込みを行う前にデータを読み込むことが必要で、読み込み/書き込みは同時に実行することが望ましいとされています。これは、異なるディスクやシステムとの間でコピーを行う場合に特に重要です。そのほか、正確な処理の予想所要時間をユーザに通知するという面でも問題が存在しました。サイズの大きな I/O では、進捗状況を計測して最新の予想所要時間を表示するためのポイントの数が相対的に少なくなるのです。さらに開発チームは、非キャッシュ I/O では、サイズの小さなファイルを大量にコピーする場合、コピー元/コピー先となるファイルの間でディスク ヘッドがディスク上を常に移動し続けることになるという重大な問題にも直面しました。

そこで開発チームは、分析、ベンチ マーキング、チューニングなどの作業を入念に行い、256 KB 以下の小さなファイルではキャッシュ I/O を使用するアルゴリズムを実装しました。ファイル サイズが 256 KB を超える場合、ファイル コピー エンジンは内部行列を使用して、非キャッシュ I/O の数とサイズを直ちに判定します。I/O の数は、2 (2 MB 未満のファイルの場合) ~ 8 (8 MB を超えるファイルの場合) となります。I/O のサイズは、ファイル サイズと同一 (1 MB のファイルの場合)、1 MB (1 MB ~ 2 MB のファイルの場合)、2 MB (2 MB を超えるファイルの場合) のいずれかになります。

たとえば、16 MB のファイルのコピーを行う場合、ファイル コピー エンジンは、キャッシュを使用せず非同期でコピー元ファイルに対して 2 MB ずつの読み取りを 8 回実行して、処理が完了するまで待機し、続いてキャッシュを使用せず非同期でコピー先ファイルに対して 2 MB ずつの書き込みを 8 回実行し、同じくその処理が完了するまで待機します。その後は同様の処理を繰り返します。次の Process Monitor のトレース画面には、16 MB のファイルをローカル システムからリモート システムへコピーする際の処理が示されています。

図 4

図 4

このアルゴリズムは、さまざまな面で以前のアルゴリズムよりも優れていましたが、それでも問題がいくつかありました。その中の 1 つは、ネットワーク上でのファイル コピーで時折発生する書き込み処理での不具合です。その一例を、次に示す書き込みのトレースに見ることができます。

図 5

図 5

書き込み処理のオフセットが 327,680 から 458,752 に飛んでおり、オフセット 393,216 のブロックがスキップされています。これによりディスク ヘッドのシークが発生し、NTFS が当該ファイルのスキップした領域にゼロを書き込むという不要な処理が実行されています。オフセット 393,216 への書き込みが 2 度実行されているのはそのためです。上の図で強調表示したイベントのスタック トレースを次に示します。NTFS がキャッシュ マネージャーの CcZeroData 関数を呼び出し、スキップしたブロックにゼロを書き込んでいます。

図 6

図 6

非キャッシュ I/O を使用するこのアルゴリズムにはさらに大きな問題がありました。公開のシナリオでパフォーマンスが損なわれるのです。たとえば、Web サイトのコンテンツの格納先であるファイル共有に複数のファイルをまとめてコピーするというシナリオでは、Web サーバーはコピー元ファイルへの初回アクセス時に、ディスクから読み取り処理を実行する必要があります。これはサーバーのみならずクライアント システムにも当てはまります。新しいファイルの存在が検出されると、デスクトップ サーチによるインデックス作成、ウイルス対策ソフトやスパイウェア対策ソフトの実行、エクスプローラーでの親ディレクトリのフォルダー アイコン用のサムネイル作成などが行われるため、ほとんどのコピー操作は公開のシナリオに該当すると言えます。

そして、このアルゴリズムの最大の欠陥であり、多くの Windows Vista ユーザーから不満の声が寄せられたのが、サイズが 256 KB ~数 10 MB のファイルを大量にまとめてコピーした場合に、体感処理速度が Windows XP よりもはるかに劣ることがあるという点でした。キャッシュ I/O を使用していた以前のアルゴリズムでは、エクスプローラーは、キャッシュ マネージャーの遅延書き込みスレッドがデータをディスクにコミットする処理を実際に行うはるか前に、メモリへのコピー内容の書き込みを完了してコピー画面を終了していました。それに対し Windows Vista では、非キャッシュ I/O を実装したために、エクスプローラーは現在の書き込み処理が完了するまで次の書き込み処理は行えず、コピーされたすべてのデータがディスクに書き込まれるまで、コピー処理の完了を通知できなくなってしまいました。また Windows Vista では、エクスプローラーは予想処理時間の測定を行うのに 12 秒待機せねばならないことに加え、測定を行うアルゴリズムはコピー速度の変化に左右されやすく、ユーザの不満をさらに増大させました。

Windows Vista SP1 での機能強化の取り組み

Windows Vista SP1 の開発において、開発チームはコピー エンジンを再度見直して機能拡張に取り組むことを決め、Windows Vista の最初のリリースで見られたコピー処理の機能低下に対し、実際のパフォーマンスと体感上のパフォーマンスの双方を改善するための方策を検討しました。最も大きな見直しは、ローカル/リモートのすべてのファイル コピーで再びキャッシュ I/O を採用するようにしたことでした (一部例外がありますが、それについてはこの後説明します)。これにより、体感上のコピー速度が向上し、公開シナリオでの動作も改善されました。ここでは、前述したようなキャッシュ I/O の問題点に対処するために、ファイル コピーのアルゴリズムとプラットフォームに関していくつかの大きな変更が行われました。

例外的にキャッシュ I/O を使用しないシナリオとしては、リモート ファイルのコピーがあります。この処理では、Windows のクライアント サイドのリモート ファイル システム ドライバーである Rdbss.sys を活用し、キャッシュが重複して実行される事態を回避しています。具体的には、リモート ファイルの読み取り/書き込みが行われる際に、ローカル システムにキャッシュを作成しないよう命令するコマンドをドライバーに対して発行します。エクスプローラーが発行したコマンドを、次の Process Monitor の画面に示します。

図 7

図 7

そのほか、リモート ファイルのコピーでは、SMB 2 のファイル システム ドライバー srv2.sys によってパイプライン化された I/O を実行できるようになりました。これは、Windows Vista のほか、Windows Server 2008 で新たに可能になった機能です。従来の SMB の実装ではネットワークで実行できる I/O のサイズの上限は 60 KB でしたが、SMB 2 ではパイプライン化された 64 KB の I/O を実行できます。これにより、アプリケーションから受け取った大規模な I/O を処理する場合に、64 KB の I/O を複数同時実行して、リモート システムと間のコピーにおけるデータの遅延を軽減できます。

さらに、このコピー エンジンでは、コピー元ファイルのサイズに応じて最小 128 KB ~ 最大 1 MB の 4 つのサイズの I/O を初回に実行でき、これにより、キャッシュ マネージャーの先読みスレッドをトリガーして大規模な I/O を処理します。Windows Vista SP1 では、プラットフォームの変更により、キャッシュ マネージャーが先読み/遅延書き込みの双方でさらに大規模な I/O を実行することが可能になっています。この機能の実現の背景には、Windows Vista の最初のリリースの I/O システムで、それまでのバージョンの Windows において上限とされていた 64 KB を超える I/O のサポートの実現があります。これにより、ディスク アクセスやディスク ヘッドのシークの回数が減少するため、ローカル コピーでのパフォーマンスの向上も達成でき、さらにキャッシュ マネージャーの遅延書き込みスレッドをコピー元ファイルのデータがメモリに書き込まれる速度に合わせて実行できるようになります。メモリが圧迫されることも少なくなり (完全にゼロになったわけではありませんが)、実際に必要とされている情報がコピー中に破棄されるようなこともなくなりました。また、リモート ファイルのコピーでは、srv2.sys により I/O のパイプライン処理を実行できます。キャッシュ マネージャーは、アプリケーションが発行する I/O の倍のサイズの読み取り I/O を実行可能で、サイズの上限は Windows Vista では 2 MB、Windows Server 2008 では 16 MB です。書き込み I/O の場合は、Windows Vista では 1 MB、Windows Server 2008 では 32 MB が上限となります。

次に、Windows Vista SP1 システム間で、サイズが 16 MB のファイルのコピーを実行した際のトレースを抜粋したものを示します。エクスプローラーによって 1 MB の I/O が実行され、キャッシュ マネージャーが 2 MB 分の先読みを実行しています。この処理には非キャッシュの I/O フラグが設定されています。

図 8

図 8

Windows Vista SP1 に加えられた以上のような変更により、Windows の過去のバージョンに比較して多くの面でパフォーマンスの向上が達成されましたが、最初の Windows Vista リリースよりもパフォーマンスの低下が見られる領域が一部あります。その 1 つは、低速なネットワーク経由での Windows Server 2003 システムとの間でのファイル コピーです。この場合、最初の Windows Vista リリースのコピー エンジンの処理のほうが高速ですが、これまでに述べたような I/O 処理に関する問題が原因で、Windows Server 2003 のキャッシュ マネージャーが異常動作し、コピー元ファイルのデータによってサーバーのメモリが占有されるという事態を招きます。Windows Vista SP1 のコピー エンジンではこの現象は回避されていますが、60 KB の I/O の代わりに 32 KB の I/O が実行されるため、高遅延接続では最初の Windows Vista リリースの半分のスループットしか達成できません。

そのほか、同一ボリューム上でサイズの大きなファイルのコピーを実行する場合も、最初の Windows Vista リリースに比較してパフォーマンスが劣ります。Windows Vista SP1 では、システムの他の領域からのディスクへのアクセスを確保してコピー時の応答性の向上を図るために、最初のリリースに比較して I/O の実行サイズが小さくなっており、そのため、コピー元ファイルの読み取り時/コピー先ファイルへの書き込み時に行われるディスク ヘッドのシーク回数が増加する可能性があります。効率的なキューイング アルゴリズムに対応していないディスクの場合、この現象が顕著に見られます。

Windows Vista SP1 に加えられた変更でもう 1 つ触れておくべき点は、Windows Vista の最初のリリースに比較して、エクスプローラーがより短時間でコピーの予想処理時間を算出できるようになり、計算アルゴリズムの精度が向上したということです。

まとめ

ファイル コピーのしくみは一見シンプルなように見えて実は複雑です。Windows Vista 製品チームは、この機能に関する Windows Vista ユーザーからのフィードバックを真摯に受け止め、多くの時間を費やしてさまざまなアプローチの評価と最終実装の調整を行い、ほとんどのコピー シナリオでパフォーマンスを以前の Windows の水準にまで回復させ、また、一部の主要シナリオにおいてはパフォーマンスの大幅な向上を実現しました。こうした変更はエクスプローラーでのコピー処理、および CopyFileEx (英語) API を使用したアプリケーションによるコピー処理に適用されます。これまでのバージョンの Windows に比較して最も大きく向上した点は、広帯域・高遅延ネットワーク経由でのファイルのコピーです。大規模な I/O のサポート、SMB 2 によるパイプライン処理、Windows Vista の新機能である TCP/IP スタック受信ウィンドウの自動調整などにより、Windows XP や Windows Server 2003 では 10 分を要していたコピーが 1 分以内で完了できるようになるという、めざましい成果を達成しています。

公開: 2008 年 2 月 4 日月曜日 6:47 PM 投稿者: markrussinovich (英語)

ページのトップへ

共有

ブログにコピー: ([Ctrl] + [C] でコピーしてください)