在 C # 中为大文件创建校验和的最快方法是什么

我必须在一些机器上同步大文件。这些文件的大小可以达到6GB。每隔几周手动同步一次。我不能考虑文件名,因为他们可以随时更改。

我的计划是在目标 PC 和源 PC 上创建校验和,然后将所有具有校验和的文件(这些文件还没有在目标 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分钟

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

124964 次浏览

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

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

同样的文件还是要花很长时间。

调用 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 ();
}

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

为了加快速度(用 SHA256在我的机器上散列2 Gb 文件需要2分钟,用 MD5需要1分钟) ,在 BufferedStream中包装 FileStream并设置合理大小的缓冲区大小(我尝试使用 ~ 1 Mb buffer) :

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

您正在执行错误操作(可能读缓冲区太小)。在一台不合时宜的机器上(2002年的 Athlon 2x1800MP) ,磁盘上的 DMA 可能不正常(6.6 M/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

1m58.832 s

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

好了,谢谢大家,让我总结一下:

  1. 使用“本机”exe 执行 散列从6分钟到 10秒钟,这是巨大的。
  2. 增加缓冲区 甚至更快-使用 MD5 in 时,1.6 GB 文件需要5.2秒。网络,所以我会用这个解决方案-再次感谢

我用缓冲区大小做了测试,运行这段代码

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

我测试了一个291.2 GB 的文件,结果是

  • 10000:369,24秒
  • 100000:362,55 s
  • 1000.000:361.53秒
  • 10.000.000:434,15s
  • 100.000.000:435,15 s
  • 1.000.000.000:434,31 s
  • 和376,22时,使用原来的,没有缓冲代码。

我正在运行一个 i52500K CPU,12 GB 内存和一个 OCZ 顶点4256 GB 固态硬盘。

所以我想,用一个标准的2TB 硬盘怎么样,结果是这样的

  • 10000:368,52秒
  • 100.000:364,15 s
  • 1.000.000:363.06 s
  • 10.000.000:678,96 s
  • 100.000.000:617.89 s
  • 1.000.000.000:626,86 s
  • 没有缓冲368,24

所以我建议要么没有缓冲区,要么缓冲区最大为1百万。

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

我确实对内置的 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);
}
}

您可以查看 XxHash.Net (https://github.com/wilhelmliao/xxHash.NET)
XxHash 算法似乎比其他所有算法都要快。
XxHash 网站上的一些基准测试: < a href = “ https://github.com/Cyan4973/xxHash”rel = “ nofollow norefrer”> https://github.com/cyan4973/xxHash

附言: 我还没用过。