C#で大きなファイルのチェックサムを作成する最も速い方法は何ですか


128

一部のマシン間で大きなファイルを同期する必要があります。ファイルのサイズは最大6GBです。同期は数週間ごとに手動で行われます。ファイル名はいつでも変更できるため、考慮に入れることはできません。

私の計画は、宛先PCとソースPCでチェックサムを作成し、チェックサムが付いていないすべてのファイルを宛先にコピーすることです。私の最初の試みは次のようなものでした:

using System.IO;
using System.Security.Cryptography;

private static string GetChecksum(string file)
{
    using (FileStream stream = File.OpenRead(file))
    {
        SHA256Managed sha = new SHA256Managed();
        byte[] checksum = sha.ComputeHash(stream);
        return BitConverter.ToString(checksum).Replace("-", String.Empty);
    }
}

問題はランタイムでした:
-SHA256で1.6 GBファイル-> 20分-MD5で1.6
GBファイル-> 6.15分

チェックサムを取得するためのより良い-より速い-方法はありますか?


2
本当にチェックサムをチェックする必要がありますか?ファイルをどのようにコピーしていますか?Windowsの場合は、Robocopyの最新バージョンを使用します...
Mesh

6
2つの候補ファイル間でファイルサイズが異なる場合にのみハッシングを行うには、ここにいいヒントがあります。stackoverflow.com/a/288756/74585
Matthew Lock

回答:


117

ここでの問題はSHA256Managed、一度に4096バイトを読み取る(ファイルストリームから読み取る量を継承FileStreamおよび上書きRead(byte[], int, int)して確認する)ことです。これは、ディスクIOには小さすぎるバッファーです。

速度を上げるには(私のマシンで2 GbファイルをSHA256でハッシュする場合は2分、MD5の場合は1分)ラップFileStreamしてBufferedStream、適度なサイズのバッファーサイズを設定します(私は〜1 Mbのバッファーで試しました)。

// Not sure if BufferedStream should be wrapped in using block
using(var stream = new BufferedStream(File.OpenRead(filePath), 1200000))
{
    // The rest remains the same
}

3
OK-これは違いをもたらしました-MD5で1.6GBファイルをハッシュすると、私のボックスで5.2秒かかりました(QuadCode @ 2.6 GHz、8GB Ram)-ネイティブ実装よりもさらに高速です...
crono

4
わかりません。私はこの提案を試しましたが、違いはごくわずかです。バッファリングなしの1024mbファイル12-14秒、バッファリングも12-14秒-数百の4kブロックを読み取るとIOが増えることを理解していますが、フレームワークまたはフレームワークの下のネイティブAPIがこれをすでに処理していないかどうか自問します..
Christian Casutt、

11
パーティーには少し遅れますが、FileStreamの場合、現在FileStream自体で既にストリームが行われているため、ストリームをBufferedStreamでラップする必要はありません。出典
Reyhn

私は、この問題を小さいファイル(10 MB未満ですが、MD5を取得するまでに時間がかかります)で処理していました。.Net 4.5を使用していますが、BufferedStreamでこのメソッドに切り替えると、8.6MBファイルのハッシュ時間が約8.6秒から<300ミリ秒に短縮されました
Taegost

1024 kBではなく、BufferedStream / w 512 kBを使用しました。1.8 GBのファイルは30秒で解決されました。
Hugo Woesthuis 2017年

61

ファイル全体をチェックサムするのではなく、100mbごとにチェックサムを作成して、各ファイルにチェックサムのコレクションが含まれるようにします。

そうすれば、チェックサムを比較するときに、最初の異なるチェックサムの後で比較を停止し、早期に取り出し、ファイル全体を処理する手間を省くことができます。

同一のファイルの場合は、まだ時間がかかります。


2
アイデアは気に入っていますが、時間の経過とともに多くの変更されていないファイルが作成されるため、このシナリオでは機能しません。
クロノ2009

1
ファイルの100MBごとにチェックサムをどのように行うのですか?
Smith

1
セキュリティ上の理由でチェックサムを使用する場合、攻撃者は除外したバイトを変更できるため、あまりお勧めできません。
b.kiener

2
+1これは、1対1の比較を実行するときに優れたアイデアです。残念ながら、私はMD5ハッシュをインデックスとして使用して、多数の重複(多対多のチェック)から一意のファイルを探しています。
Nathan Goings 2018

1
@ b.kienerバイトは除外されません。あなたは彼を誤解した。
Soroush Falahati

47

Anton Gogolevが指摘したように、FileStreamはデフォルトで一度に4096バイトを読み取りますが、FileStreamコンストラクターを使用して他の値を指定できます。

new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 16 * 1024 * 1024)

MicrosoftのBrad Abramsが2004年に書いたことに注意してください。

FileStreamの周りにBufferedStreamをラップすることによる利点はありません。4年ほど前にBufferedStreamのバッファリングロジックをFileStreamにコピーして、デフォルトのパフォーマンスを向上させました。

ソース


22

md5sum.exeのWindowsポートを呼び出します。.NET実装の約2倍の速さ(少なくとも私のマシンでは1.2 GBのファイルを使用)

public static string Md5SumByProcess(string file) {
    var p = new Process ();
    p.StartInfo.FileName = "md5sum.exe";
    p.StartInfo.Arguments = file;            
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.Start();
    p.WaitForExit();           
    string output = p.StandardOutput.ReadToEnd();
    return output.Split(' ')[0].Substring(1).ToUpper ();
}

3
WOW-pc-tools.net/win32/md5sumsからmd5sums.exeを使用すると、非常に高速になります。1681457152バイト、8672ミリ秒= 184.91 MB /秒-> 1,6GB〜9秒これは私の目的には十分高速です。
クロノ2009

16

わかりました-皆さんのおかげで、これをまとめましょう:

  1. 「ネイティブ」のexeを使用してハッシュを行うと、6分から10秒の時間を要し、非常に時間がかかります。
  2. バッファの増加はさらに速くなりました-1.6GBファイルは.NetのMD5を使用して5.2秒かかったので、このソリューションを使用します-もう一度ありがとう

10

このコードを実行して、バッファサイズをテストしました

using (var stream = new BufferedStream(File.OpenRead(file), bufferSize))
{
    SHA256Managed sha = new SHA256Managed();
    byte[] checksum = sha.ComputeHash(stream);
    return BitConverter.ToString(checksum).Replace("-", String.Empty).ToLower();
}

そして、サイズが29½GBのファイルでテストしたところ、結果は

  • 10.000:369,24秒
  • 100.000:362,55秒
  • 1.000.000:361,53秒
  • 10.000.000:434、15秒
  • 100.000.000:435,15秒
  • 1.000.000.000:434,31秒
  • また、オリジナルのバッファリングされたコードを使用した場合、376,22秒。

私はi5 2500K CPU、12 GBラム、OCZ Vertex 4 256 GB SSDドライブを実行しています。

だから、標準の2TBハードドライブはどうだろうと思った。そして結果はこのようなものでした

  • 10.000:368,52秒
  • 100.000:364,15秒
  • 1.000.000:363,06秒
  • 10.000.000:678,96秒
  • 100.000.000:617,89秒
  • 1.000.000.000:626,86秒
  • そして、誰もバッファリングされていない368,24

だから私はバッファーなしか、最大1ミルのバッファーをお勧めします。


わかりません。このテストは、Anton Gogolevから受け入れられた回答とどのように矛盾しますか?
buddybubble 2014年

データの各フィールドの説明を追加できますか?
videoguy 2015

2

何かがおかしい(おそらく読み取りバッファが小さすぎる)。時代遅れのマシン(2002年からのAthlon 2x1800MP)で、ディスク上にDMAがたぶんあると思われます(シーケンシャルリードを実行すると、6.6M /秒は非常に遅くなります)。

「ランダム」データを含む1Gファイルを作成します。

# dd if=/dev/sdb of=temp.dat bs=1M count=1024    
1073741824 bytes (1.1 GB) copied, 161.698 s, 6.6 MB/s

# time sha1sum -b temp.dat
abb88a0081f5db999d0701de2117d2cb21d192a2 *temp.dat

1分5.299秒

# time md5sum -b temp.dat
9995e1c1a704f9c1eb6ca11e7ecb7276 *temp.dat

1分58.832秒

これも奇妙です。md5は、私にとっては常にsha1よりも低速です(数回再実行されます)。


はい。AntonGogolevのように、バッファを増やします。「ネイティブ」のMD5.exeを実行したところ、1,6 GBのファイルで9秒かかりました。
クロノ2009

2

私はパーティーに遅れるが、実際にソリューションを実装する前にテストを実行したことを知っています。

組み込みのMD5クラスとmd5sum.exeに対してもテストを実行しました。私の場合、組み込みのクラスは13秒かかりましたが、実行ごとにmd5sum.exeが約16〜18秒かかりました。

    DateTime current = DateTime.Now;
    string file = @"C:\text.iso";//It's 2.5 Gb file
    string output;
    using (var md5 = MD5.Create())
    {
        using (var stream = File.OpenRead(file))
        {
            byte[] checksum = md5.ComputeHash(stream);
            output = BitConverter.ToString(checksum).Replace("-", String.Empty).ToLower();
            Console.WriteLine("Total seconds : " + (DateTime.Now - current).TotalSeconds.ToString() + " " + output);
        }
    }

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.