🎃

C#で巨大なファイルを操作する時の知見

2023/06/08に公開

ファイルの書き込み・読み込みは遅い。
なので、メモリに余裕があるなら直接ファイルに読み書きするより、一旦メモリに引き上げて処理したほうが早い。

書き込みは一旦MemoryStreamとかに書いてからCopyToAsyncでまとめて書き込むのが早い。
File.WriteAllLinesAsyncはかなり遅かった。多分内部で1行書くごとにawaitしてそうな気がする(確認はしてない)。
読込は File.ReadLines() がいいかも。

以下はCSVを加工するサンプル。Ryzen 5800Xで6.5万行を加工するのに3秒くらい。

// 読込はReadLinesで1行ずつ
var lines = File.ReadLines(inputFile);

// 各行を加工(遅延実行)
var result = lines
    .AsParallel().WithDegreeOfParallelism(Environment.ProcessorCount)
    .Select(ProcessLine);

// 書き出しはファイルにではなく、一旦メモリに書き込み
// 1行ずつ書きたいので、StreamWriterを使う
using var memoryStream = new MemoryStream();
using var memoryWriter = new StreamWriter(memoryStream, Encoding.UTF8);
foreach (var line in result)
{
    await memoryWriter.WriteLineAsync(line);
}

// 書き出しは一気にMemoryStreamからコピーしてしまう
// await File.WriteAllLines(result); で 30秒かかってたのが1秒未満になった
using var fileWriter = new FileStream(outputFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);
await memoryStream.CopyToAsync(fileWriter);

一番遅いのはファイル読み込みなので、もっと早くできるかも?
一旦 List<string> に引き上げたけど、Listの生成自体に時間がかかっててたので辞めた(0.5秒かかってた)。

Discussion