4

ゲームのデータ ファイルを解析するための単純なライブラリを作成しているときに、データ ファイル全体をメモリに読み込み、そこから解析する方がはるかに高速であることに気付きました (最大 15 倍、106 秒対 7 秒)。

解析は通常シーケンシャルに行われますが、ファイル内の別の場所に格納されているオフセットでリンクされたデータを読み取るために、時々シークが実行されます。

メモリからの解析は間違いなく高速になることはわかっていますが、違いが非常に大きい場合は何かが間違っています。これをシミュレートするためにいくつかのコードを書きました:

public static void Main(string[] args)
{
    Stopwatch n = new Stopwatch();

    n.Start();
    byte[] b = File.ReadAllBytes(@"D:\Path\To\Large\File");
    using (MemoryStream s = new MemoryStream(b, false))
        RandomRead(s);
    n.Stop();
    Console.WriteLine("Memory read done in {0}.", n.Elapsed);
    b = null;
    n.Reset();
    n.Start();
    using (FileStream s = File.Open(@"D:\Path\To\Large\File", FileMode.Open))
        RandomRead(s);
    n.Stop();
    Console.WriteLine("File read done in {0}.", n.Elapsed);
    Console.ReadLine();
}
private static void RandomRead(Stream s)
{
    // simulate a mostly sequential, but sometimes random, read
    using (BinaryReader br = new BinaryReader(s)) {
        long l = s.Length;
        Random r = new Random();
        int c = 0;
        while (l > 0) {
            l -= br.ReadBytes(r.Next(1, 5)).Length;
            if (c++ <= r.Next(10, 15)) continue;
            // simulate seeking
            long o = s.Position;
            s.Position = r.Next(0, (int)s.Length);
            l -= br.ReadBytes(r.Next(1, 5)).Length;
            s.Position = o;
            c = 0;
        }
    }
}

これへの入力として、ゲームのデータ ファイルの 1 つを使用しました。そのファイルは約 102 MB で、Memory read done in 00:00:03.3092618. File read done in 00:00:32.6495245.ファイルよりも約 11 倍高速なメモリ読み取りを行うこの結果 ( ) が生成されました。

ファイルキャッシュを介して速度を向上させるために、ファイル読み取りの前にメモリ読み取りが行われました。それはまだずっと遅いです。

FileStreamのバッファ サイズを増減してみました。大幅に良い結果が得られるものは何もなく、増減しすぎると速度が低下するだけでした。

私が間違っていることはありますか、それともこれは予想されることですか? 少なくとも減速をそれほど重要でないようにする方法はありますか?

ファイル全体を一度に読み取ってから解析すると、読み取りと解析を同時に行うよりもはるかに高速なのはなぜですか?

私は実際に、Windows ネイティブを使用してファイルを読み取る C++ で記述された同様のライブラリと比較しましたが、CreateFileMapping非常MapViewOfFileに高速です。マネージドからアンマネージドへの絶え間ない切り替えと、これを引き起こす関連するマーシャリングでしょうか?

MemoryMappedFile.NET 4 に存在する sも試しました。速度の向上はわずか約 1 秒でした。

4

3 に答える 3

3

私が間違っていることはありますか、それともこれは予想されることですか?

いいえ、何も問題ありません。これは完全に予想されます。ディスクへのアクセスがメモリへのアクセスよりも桁違いに遅いことは、理にかなっています。


アップデート:

ファイルの 1 回の読み取りとそれに続く処理は、読み取り中の処理よりも高速であることも期待されます。

大規模な IO 操作とメモリ内での処理は、ディスクからビットを取得して処理し、ディスクを再度呼び出して (IO が完了するのを待って)、そのビットを処理するよりも高速です。

于 2012-05-10T13:03:21.913 に答える
2

私が間違っていることはありますか、それともこれは予想されることですか?

ハードディスクは、RAM に比べてアクセス時間が非常に長くなります。シーケンシャル リードは非常に高速ですが、(データが断片化されているため) ヘッドが移動する必要があるとすぐに、アプリケーションがアイドリングしている間、次のデータ ビットを取得するのに数ミリ秒かかります。

少なくとも減速をそれほど重要でないようにする方法はありますか?

SSDを購入。

また、Memory-Mapped Files for .NETもご覧ください。

MemoryMappedFile.CreateFromFile().


あなたの編集に関しては、@Odedを使用して、事前にファイルを読み取るとペナルティが追加されると言います。ただし、最初にファイル全体を読み取るメソッドが 'process-as-you-read' の 7 倍遅くなることはありません。

于 2012-05-10T13:04:57.683 に答える
0

C ++とC#でファイルを読み取るさまざまな方法を比較するいくつかのベンチマークを実行することにしました。まず、256MBのファイルを作成しました。C ++ベンチマークでは、バッファリングとは、最初にファイル全体をバッファにコピーしてから、バッファからデータを読み取ることを意味します。すべてのベンチマークは、ファイルを直接または間接的にバイトごとに順番に読み取り、チェックサムを計算します。すべての時間は、ファイルを開いてから完全に終了してファイルを閉じるまでの時間を測定します。一貫したOSファイルのキャッシュを維持するために、すべてのベンチマークが複数回実行されました。

C ++
バッファなしメモリマップトファイル:300ms
バッファ付きメモリマップトファイル:500ms
バッファなしfread:23,000ms
バッファ付きfread:500ms
バッファなしifstream:26,000ms
バッファ付きifstream:700ms

C#
MemoryMappedFile:112,000ms
FileStream:2,800ms
MemoryStream:2,300ms
ReadAllBytes:600ms

必要に応じてデータを解釈します。C#のメモリマップトファイルは、最悪の場合のc ++コードよりも低速ですが、c++のメモリマップトファイルは最速です。C#のReadAllBytesはまともな速度で、c++のメモリマップトファイルの2倍の速度しかありません。したがって、最高のパフォーマンスが必要な場合は、ReadAllBytesを使用してから、ストリームを使用せずに配列から直接データにアクセスすることをお勧めします。

于 2012-05-10T14:42:21.990 に答える