137

我必须在某些机器上同步大文件。文件最大可达 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 分钟

有没有更好更快的方法来获取校验和(也许有更好的散列函数)?

4

9 回答 9

124

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

为了加快速度(在我的机器上使用 SHA256 散列 2 Gb 文件需要 2 分钟,MD5 需要 1 分钟)换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
}
于 2009-07-24T13:41:16.980 回答
72

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

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

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

于 2009-07-24T13:26:40.837 回答
54

正如 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 中,以鼓励更好的默认性能

来源

于 2015-01-17T13:42:57.883 回答
23

调用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 ();
}
于 2009-07-24T13:37:00.387 回答
17

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

  1. 使用“本机”exe进行散列需要 6 分钟到 10 秒的时间,这是巨大的。
  2. 增加缓冲区甚至更快 - 1.6GB 文件在 .Net 中使用 MD5 需要 5.2 秒,所以我会采用这个解决方案 - 再次感谢
于 2009-07-24T14:26:49.397 回答
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,15s
  • 100.000.000: 435,15s
  • 1.000.000.000: 434,31s
  • 使用原始的无缓冲代码时为 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,06s
  • 10.000.000: 678,96s
  • 100.000.000: 617,89s
  • 1.000.000.000: 626,86s
  • 对于没有缓冲的 368,24

所以我建议要么不使用缓冲区,要么建议使用最大 1 磨机的缓冲区。

于 2012-10-07T19:38:28.423 回答
4

我知道我迟到了,但在实际实施解决方案之前进行了测试。

我确实对内置的 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);
        }
    }
于 2019-03-16T13:46:33.037 回答
3

你可以看看 XxHash.Net ( https://github.com/wilhelmliao/xxHash.NET )
xxHash 算法似乎比其他算法更快。
xxHash 网站上的一些基准测试:https ://github.com/Cyan4973/xxHash

PS:我还没用过。

于 2020-01-22T00:59:24.993 回答
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

1m5.299s

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

1m58.832s

这也很奇怪,对我来说 md5 一直比 sha1 慢(重新运行了几次)。

于 2009-07-24T13:56:37.727 回答