ディスクからデータをより効率的に読み取る方法(.Netがあると仮定)



こんにちは、Habr! しばらく前、私は質問に興味がありました:ディスクからデータを読み取る最も効率的な方法は何ですか(.Netがある場合)? 多数のファイルを読み取るタスクは、多くのプログラムで見られます。多くのプログラムは、最初から構成の読み取りを開始し、一部のロードモジュールを独自に読み込みます。

インターネットでは、このような比較は見つかりませんでした(特定の構成のチューニングを除く)。

結果はGithubで見ることができます: SSDHDD

読み取り方法とテストアルゴリズム


基本的な方法がいくつかあります。


SSDとHDDですべてをテストしました(最初のケースではXeon 24コアと16 GBのメモリとIntel SSDを搭載したコンピューターがあり、2つ目はCore i5、4 GBのRAMと5400 rpmのHDDを搭載したMac Mini MGEM2LL / Aでした)。 システムは、結果に応じて、比較的新しいシステムではなく、比較的新しいシステムで最も適切に動作する方法を理解できるようになっています。

プロジェクトはここで表示できます 。これは、1つのメインTestsHost実行可能ファイルと、シナリオ*という名前の一連のプロジェクトです。 各テストは次のとおりです。

  1. 正味時間を計算するexeファイルを実行します。

  2. 1秒に1回、プロセッサの負荷、RAMの消費、ディスクの負荷、およびその他の多数の派生パラメーターがチェックされます( パフォーマンスカウンターを使用)。

  3. 結果は記憶され、テストは数回繰り返されます。 最終結果は、最大値と最小値を除いた平均時間です。

テストの準備は複雑です。 したがって、開始する前に:

  1. ファイルのサイズと数を決定します(ディスクキャッシュの影響を抑えるために、合計ボリュームがRAMサイズよりも大きくなるように選択しました)。

  2. コンピューター上で特定のサイズのファイルを探しています(同時に、以下で説明するアクセスできないファイルといくつかの特別なフォルダーを無視します)。

  3. ファイルのセットに対してテストの1つを実行しますが、結果は無視します。 これはすべて、OSキャッシュをリセットし、以前のテストから影響を取り除き、システムをウォームアップするために必要です。

エラー処理を忘れないでください:

  1. プログラムは、すべてのファイルが読み取られた場合にのみ、戻りコード0を返します。

  2. システムが突然ファイルの読み取りを開始すると、テスト全体がクラッシュする場合があります。 ため息をついて再起動し、無視したファイルにファイル(またはフォルダー)を追加します。 WindowsのProgram Filesディレクトリをファイルの優れたソースとして使用しているため、現実的にはディスク全体に広がっているため、一部のファイルはしばらくロックされる可能性があります。

  3. たとえば、プロセスが既に終了しているため、1つのパフォーマンスカウンターがエラーをスローすることがあります。 この場合、この秒のすべてのカウンターは無視されます。

  4. 大きなファイルでは、一部のテストでメモリ不足例外が安定してスローされました。 結果からそれらを削除しました。

さらに、負荷テストに関する標準的な瞬間:

  1. コンパイル-MSVSのリリースモード。 起動は、デバッガーなどを使用しない別個のアプリケーションとして実行されます。チェックの本質は、通常のソフトウェアでファイルをより速く読み取る方法であるため、チューニングはありません。

  2. ウイルス対策が無効になり、システムの更新が停止し、アクティブなプログラムも停止します。 同じ理由で、これ以上チューニングはありませんでした。

  3. 各テストは、個別のプロセスの開始です。 オーバーヘッドはエラーの範囲内(つまり、jit、プロセスの開始に費やしたなど)であることが判明したため、このような隔離だけを残しました。

  4. 一部のパフォーマンスカウンターでは、HDD / SSDの結果が常にゼロでした。 カウンターのセットがプログラムに縫い込まれているので、私はそれらを残しました。

  5. すべてのプログラムはx64として起動しました。スワップを試みるとメモリが非効率になり、実行時間が長いために統計がすぐに低下しました。

  6. スレッドの優先度と他のチューニングは使用されませんでした。最大値を厳密に絞ろうとする試みがなかったためです(これは非常に多くの要因に大きく依存します)。
  7. テクノロジー:.Net 4.6、x64

結果


ヘッダーに書いたように、結果はGithubにあります: SSDHDD

SSDドライブ


最小ファイルサイズ(バイト):2、最大サイズ(バイト):25720320、平均サイズ(バイト):40953.1175
スクリプト
時間
ScenarioAsyncWithMaxParallelCount4
00:00:00.2260000
ScenarioAsyncWithMaxParallelCount8
00:00:00.5080000
ScenarioAsyncWithMaxParallelCount16
00:00:00.1120000
ScenarioAsyncWithMaxParallelCount24
00:00:00.1540000
ScenarioAsyncWithMaxParallelCount32
00:00:00.2510000
ScenarioAsyncWithMaxParallelCount64
00:00:00.5240000
ScenarioAsyncWithMaxParallelCount128
00:00:00.5970000
ScenarioAsyncWithMaxParallelCount256
00:00:00.7610000
ScenarioSyncAsParallel
00:00:00.9340000
ScenarioReadAllAsParallel
00:00:00.3360000
シナリオ非同期
00:00:00.8150000
シナリオAsync2
00:00:00.0710000
ScenarioNewThread
00:00:00.6320000

したがって、多くの小さなファイルを読み取るとき、2つの勝者は非同期操作です。 実際、どちらの場合でも.Netは31スレッドを使用しました。

実際、両方のプログラムは、ScenarioAsyncWithMaxParallelCount32のActionBlockの有無(制限付き)で異なっていたため、読み取りを制限しない方がよいため、より多くのメモリが使用され(私の場合は1.5回)、制限は単に標準設定レベルになります(スレッドプールはコアの数などに依存するため)

最小ファイルサイズ(バイト):1001、最大サイズ(バイト):25720320、平均サイズ(バイト):42907.8608
スクリプト
時間
ScenarioAsyncWithMaxParallelCount4
00:00:00.4070000
ScenarioAsyncWithMaxParallelCount8
00:00:00.2210000
ScenarioAsyncWithMaxParallelCount16
00:00:00.1240000
ScenarioAsyncWithMaxParallelCount24
00:00:00.2430000
ScenarioAsyncWithMaxParallelCount32
00:00:00.3180000
ScenarioAsyncWithMaxParallelCount64
00:00:00.5100000
ScenarioAsyncWithMaxParallelCount128
00:00:00.7270000
ScenarioAsyncWithMaxParallelCount256
00:00:00.8190000
ScenarioSyncAsParallel
00:00:00.7590000
ScenarioReadAllAsParallel
00:00:00.3120000
シナリオ非同期
00:00:00.5080000
シナリオAsync2
00:00:00.0670000
ScenarioNewThread
00:00:00.6090000

最小ファイルサイズを大きくすると、次のようになります。

  1. リーダーは、プロセッサコアの数に近いスレッド数でプログラムを開始しました。
  2. 一連のテストでは、スレッドの1つがロックが解放されるのを常に待っていました( パフォーマンスカウンター「同時キューの長さ」を参照)。
  3. ディスクからの同期読み取り方法は、依然として部外者です。

最小ファイルサイズ(バイト):10007、最大サイズ(バイト):62 444 171、平均サイズ(バイト):205102.2773
スクリプト
時間
ScenarioAsyncWithMaxParallelCount4
00:00:00.6830000
ScenarioAsyncWithMaxParallelCount8
00:00:00.5440000
ScenarioAsyncWithMaxParallelCount16
00:00:00.6620000
ScenarioAsyncWithMaxParallelCount24
00:00:00.8690000
ScenarioAsyncWithMaxParallelCount32
00:00:00.5630000
ScenarioAsyncWithMaxParallelCount64
00:00:00.2050000
ScenarioAsyncWithMaxParallelCount128
00:00:00.1600000
ScenarioAsyncWithMaxParallelCount256
00:00:00.4890000
ScenarioSyncAsParallel
00:00:00.7090000
ScenarioReadAllAsParallel
00:00:00.9320000
シナリオ非同期
00:00:00.7160000
シナリオAsync2
00:00:00.6530000
ScenarioNewThread
00:00:00.4290000

そして、SSDの最後のテスト:10 KBからのファイル、それらの数は小さいが、それら自体は大きい。 結果として:

  1. スレッドの数を制限しない場合、読み取り時間は同期操作に近くなります
  2. 制限は(コアの数)* [2.5-5.5]としてより望ましいです

HDDドライブ


SSDですべてが多かれ少なかれ良ければ、私はより頻繁にドロップしたので、クラッシュしたプログラムの結果の一部を除外しました。

最小ファイルサイズ(バイト):1001、最大サイズ(バイト):54989002、平均サイズ(バイト):210818,0652
スクリプト
時間
ScenarioAsyncWithMaxParallelCount4
00:00:00.3410000
ScenarioAsyncWithMaxParallelCount8
00:00:00.3050000
ScenarioAsyncWithMaxParallelCount16
00:00:00.2470000
ScenarioAsyncWithMaxParallelCount24
00:00:00.1290000
ScenarioAsyncWithMaxParallelCount32
00:00:00.1810000
ScenarioAsyncWithMaxParallelCount64
00:00:00.1940000
ScenarioAsyncWithMaxParallelCount128
00:00:00.4010000
ScenarioAsyncWithMaxParallelCount256
00:00:00.5170000
ScenarioSyncAsParallel
00:00:00.3120000
ScenarioReadAllAsParallel
00:00:00.5190000
シナリオ非同期
00:00:00.4370000
シナリオAsync2
00:00:00.5990000
ScenarioNewThread
00:00:00.5300000

小さなファイルの場合、リーダーは再び非同期読み取りです。 ただし、同期操作も良い結果を示しました。 答えはディスクの負荷、つまり同時読み取りの制限にあります。 多くのスレッドで強制的に読み取りを開始しようとすると、システムは読み取りのために大きなキューに置かれます。 その結果、並列作業の代わりに、多くの要求を並列に処理しようとすることに時間がかかります。

最小ファイルサイズ(バイト):1001、最大サイズ(バイト):54989002、平均サイズ(バイト):208913,2665
スクリプト
時間
ScenarioAsyncWithMaxParallelCount4
00:00:00.6880000
ScenarioAsyncWithMaxParallelCount8
00:00:00.2160000
ScenarioAsyncWithMaxParallelCount16
00:00:00.5870000
ScenarioAsyncWithMaxParallelCount32
00:00:00.5700000
ScenarioAsyncWithMaxParallelCount64
00:00:00.5070000
ScenarioAsyncWithMaxParallelCount128
00:00:00.4060000
ScenarioAsyncWithMaxParallelCount256
00:00:00.4800000
ScenarioSyncAsParallel
00:00:00.4680000
ScenarioReadAllAsParallel
00:00:00.4680000
シナリオ非同期
00:00:00.3780000
シナリオAsync2
00:00:00.5390000
ScenarioNewThread
00:00:00.6730000

中サイズのファイルの場合、スレッド数をさらに低い値に制限することが望ましいことを除いて、非同期読み取りの結果は引き続き良好です。

最小ファイルサイズ(バイト):10008、最大サイズ(バイト):138634176、平均サイズ(バイト):429888,6019
スクリプト
時間
ScenarioAsyncWithMaxParallelCount4
00:00:00.5230000
ScenarioAsyncWithMaxParallelCount8
00:00:00.4110000
ScenarioAsyncWithMaxParallelCount16
00:00:00.4790000
ScenarioAsyncWithMaxParallelCount24
00:00:00.3870000
ScenarioAsyncWithMaxParallelCount32
00:00:00.4530000
ScenarioAsyncWithMaxParallelCount64
00:00:00.5060000
ScenarioAsyncWithMaxParallelCount128
00:00:00.5810000
ScenarioAsyncWithMaxParallelCount256
00:00:00.5540000
ScenarioReadAllAsParallel
00:00:00.5850000
シナリオ非同期
00:00:00.5530000
シナリオAsync2
00:00:00.4440000

繰り返しますが、リーダーは非同期操作であり、並列操作の数に制限があります。 さらに、推奨されるスレッド数はさらに少なくなりました。 そして、並列同期読み取りは着実にメモリ不足を示し始めました。

ファイルサイズが大きくなると、同時読み取り数に制限のないスクリプトはメモリ不足に陥る可能性が高くなります。 結果は発売から発売まで安定していなかったので、私はすでにそのようなテストは不適切であると考えました。

まとめ


これらのテストからどのような結果を収集できますか?

Source: https://habr.com/ru/post/J331668/


All Articles