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

问题描述 投票:122回答:8

我必须在一些机器上同步大文件。这些文件最大可达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分钟 - 使用MD5和1.6 GB文件 - > 6.15分钟

是否有更好 - 更快 - 获得校验和的方法(可能具有更好的散列函数)?

c# .net large-files checksum
8个回答
111
投票

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

为了加快速度(使用SHA256在我的机器上散步2 Gb文件需要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
}

60
投票

不要校验整个文件,每100mb左右创建校验和,因此每个文件都有一组校验和。

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

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


40
投票

正如Anton Gogolev所指出的,FileStream默认情况下一次读取4096个字节,但您可以使用FileStream构造函数指定任何其他值:

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

请注意,2004年Microsoft的Brad Abrams写道:

围绕FileStream包装BufferedStream没有任何好处。我们大约4年前将BufferedStream的缓冲逻辑复制到FileStream中,以鼓励更好的默认性能

source


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

15
投票

好的 - 多亏你们所有人 - 让我把它包起来:

  1. using a "native" exe做哈希的时间从6分钟到10秒,这是巨大的。
  2. Increasing the buffer甚至更快 - 在.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,55s
  • 1.000.000:361,53s
  • 10.000.000:434,15s
  • 100.000.000:435,15s
  • 1.000.000.000:434,31s
  • 使用原始的无缓冲代码时为376,22s。

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

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

  • 10.000:368,52s
  • 100.000:364,15s
  • 1.000.000:363,06s
  • 10,000,000:678,库图兹
  • 100.000.000:617,89s
  • 1.000.000.000:626,86s
  • 并且没有缓冲368,24

所以我建议不要缓冲或最大1磨的缓冲。


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慢(reran几次)。


0
投票

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

我确实对内置的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);
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.