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