在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);
    }
}

问题是运行时:
-带有1.6 GB文件的SHA256-> 20分钟
-带有1.6 GB文件的MD5-> 6.15分钟

是否有更好(更快)的方法来获取校验和(也许具有更好的哈希函数)?


2
您真的需要检查Checksum吗?您如何复制文件?如果您在Windows上,我会使用最新版本的Robocopy ...
Mesh

6
这里的一个很好的提示是,如果两个候选文件之间的文件大小不同,则只能打乱哈希stackoverflow.com/a/288756/74585
Matthew Lock

Answers:


117

这里的问题是一次SHA256Managed读取4096个字节(继承FileStream并覆盖)Read(byte[], int, int)以查看它从文件流中读取了多少字节),这对于磁盘IO而言太小了。

为了加快速度(2分钟,我的机器SHA256,1分钟MD5哈希上2 GB的文件)裹FileStreamBufferedStream,并设置合理大小的缓冲区大小(我试过〜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
好的-这引起了很大的差异-在我的机器上用MD5对1.6GB的文件进行哈希处理花了5.2秒的时间(QuadCode @ 2.6 GHz,8GB的Ram)-甚至比本地实现更快……
crono

4
我不明白。我只是尝试了这个建议,但区别很小甚至没有。1024mb文件,没有缓冲12-14秒,也有12-14秒缓冲-我知道读取数百个4k块会产生更多的IO,但我问自己是框架还是该框架下的本机API是否已解决此问题..
Christian Casutt

11
聚会晚了一点,但是对于FileStreams,不再需要将流包装在BufferedStream中,因为如今已经在FileStream本身中完成了。消息来源
雷恩

我只是用较小的文件(<10MB,但是要花很多时间才能获得MD5)来解决这个问题。即使我使用.Net 4.5,对于8.6MB的文件,使用BufferedStream切换到此方法也可以将哈希时间从大约8.6秒减少到<300 ms
Taegost

我使用BufferedStream / w 512 kB而不是1024 kB。1.8 GB的文件在30秒内解决。
Hugo Woesthuis

61

不要对整个文件进行校验和,每100mb左右创建校验和,因此每个文件都有校验和的集合。

然后,在比较校验和时,您可以在第一个不同的校验和之后停止比较,尽早退出,从而避免处理整个文件。

相同的文件仍然需要花费全部时间。


2
我喜欢这个主意,但是在我的情况下它不起作用,因为随着时间的推移,我最终会得到许多未更改的文件。
crono

1
您如何每文件100mb校验和?
史密斯,

1
出于安全原因,使用校验和时不是一个好主意,因为攻击者可以更改您已排除的字节。
b.kiener

2
+1当您进行一对一比较时,这是一个绝妙的主意。不幸的是,我使用MD5哈希作为索引来查找许多重复项中的唯一文件(多对多检查)。
Nathan Goings

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年写道:

将BufferedStream包裹在FileStream周围有零收益。大约4年前,我们将BufferedStream的缓冲逻辑复制到FileStream中,以鼓励更好的默认性能

资源


22

调用md5sum.exe的Windows端口。它大约是.NET实施速度的两倍(至少在使用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
哇-使用pc-tools.net/win32/md5sums中的md5sums.exe可以使它运行起来非常快。1681457152字节,8672毫秒= 184.91 MB /秒-> 1,6GB〜9秒这将足够快达到我的目的。
crono

16

好的-感谢大家-让我总结一下:

  1. 使用“本地” exe进行哈希处理需要花费6分钟到10秒的时间,这是巨大的。
  2. 增加缓冲区的速度甚至更快-使用.Net中的MD5,1.6GB文件耗时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,53s
  • 10.000.000:434,15秒
  • 100.000.000:435,15秒
  • 1.000.000.000:434,31秒
  • 使用原始的无缓冲代码时为376,22s。

我正在运行i5 2500K CPU,12 GB内存和OCZ Vertex 4 256 GB SSD驱动器。

所以我想,标准的2TB硬盘呢?结果是这样的

  • 10.000:368,52秒
  • 100.000:364,15秒
  • 1.000.000:363,06s
  • 10.000.000:678,96s
  • 100.000.000:617,89s
  • 1.000.000.000:626,86秒
  • 没有缓冲368,24

因此,我建议不要使用缓冲区,也不要建议使用最大1毫秒的缓冲区。


我不明白。该测试如何与Anton Gogolev接受的答案相矛盾?
buddybubble

您可以在数据中添加每个字段的描述吗?
videoguy 2015年

2

您做错了(读取缓冲区可能太小)。在年龄不佳的机器(2002年的Athlon 2x1800MP)上,其磁盘上的DMA可能已耗尽(连续读取时,6.6M / s的速度太慢了):

使用“随机”数据创建一个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慢(重播几次)。


是的-我会尝试增加缓冲区-就像Anton Gogolev感到不适一样。我通过“本机” MD5.exe运行了该文件,耗时9秒耗费了1.6 GB文件。
crono

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);
        }
    }

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.