C# 提高SHA-1 ComputeHash的性能

C# 提高SHA-1 ComputeHash的性能,c#,.net,performance,C#,.net,Performance,我使用下面的代码对一个文件进行校验和,结果很好。但是当我为一个大文件生成一个散列时,比如说2GB,速度相当慢。如何提高此代码的性能 fs = new FileStream(txtFile.Text, FileMode.Open); formatted = string.Empty; using (SHA1Managed sha1 = new SHA1Managed()) { byte[] hash = sha1.Comput

我使用下面的代码对一个文件进行校验和,结果很好。但是当我为一个大文件生成一个散列时,比如说2GB,速度相当慢。如何提高此代码的性能

fs = new FileStream(txtFile.Text, FileMode.Open);
        formatted = string.Empty;
        using (SHA1Managed sha1 = new SHA1Managed())
        {
            byte[] hash = sha1.ComputeHash(fs);

            foreach (byte b in hash)
            {
                formatted += b.ToString("X2");
            }
        }
        fs.Close();
更新:

系统:

操作系统:WIN7 64位,CPU:I5750,RAM:4GB,硬盘:7200rpm

测试:

测试1=59.895秒


Test2=59.94秒

那么,它是IO绑定还是CPU绑定?如果它是CPU受限的,我们对此无能为力

使用不同的参数打开
FileStream
可能会允许文件系统进行更多的缓冲,或者假设您将按顺序读取文件,但我怀疑这会有多大帮助。(如果它是CPU受限的,肯定不会有太大的作用。)

“相当慢”到底有多慢?与复制文件相比


如果您有大量内存(例如4GB或更大),当文件可能在文件系统缓存中时,再次散列文件需要多长时间?

首先,您是否测量到“相当慢”?从一开始,SHA-1的速度大约是MD5的一半,速度大约为100MB/s(取决于CPU),因此2GB的哈希运算大约需要20秒。另外,请注意,如果您使用的硬盘速度较慢,这可能是您真正的瓶颈,因为30-70 MB/s并不罕见


为了加快速度,您可能不会散列整个文件,而是散列它的第一个xKB或可表示部分(最有可能不同的部分)。如果您的文件不太相似,则不会导致重复。

第一个问题是您需要此校验和做什么。如果您不需要加密属性,那么非加密散列或加密安全性较低的散列(MD5被“破坏”并不妨碍它成为一个好的散列,也不足以用于某些用途)的性能可能会更高。您可以通过读取数据的一个子集来创建自己的哈希(我建议将此子集放在基础文件的4096字节块中工作,因为这将与SHA1Managed使用的缓冲区大小相匹配,并且允许更快地读取数据块,如果您确实为某个X值指定每X字节一次的话)

编辑:一个向上投票提醒我这个答案,同时也提醒我,我写了一篇文章,它提供了高性能的32、64和128位散列,这些散列不是加密的,但有利于提供针对错误、存储等的校验和(这反过来又提醒我应该更新它以支持.NET Core)

当然,如果您想让文件的SHA-1与其他内容进行互操作,那么您就被卡住了


我将使用不同的缓冲区大小进行实验,因为增加filestream缓冲区的大小可以以额外内存为代价提高速度。我建议使用4096的整数倍(顺便说一句,默认值是4096),因为SHA1Managed一次会请求4096个块,这样一来,任何一个FileStream返回的值都不会低于请求的最大值(允许但有时不太理想)或者一次复制多个副本。

首先:SHA-1文件哈希应该在非古老的CPU上进行I/O绑定,而I5当然不符合古老的条件。当然,这取决于SHA-1的实施,但我怀疑SHA1Managed是否缓慢

其次,2GB数据的60秒速度约为34MB/s,这对于硬盘读取来说是很慢的;即使是一个2.5英寸的笔记本电脑磁盘也可以读得更快。假设硬盘是内部的(没有USB2/或任何网络瓶颈),并且没有太多其他磁盘I/O活动,我会惊讶地看到从一个现代硬盘读取的速度低于60MB/s


我的猜测是,
ComputeHash()
在内部使用了一个小缓冲区。请尝试手动读取/哈希,以便指定一个更大的缓冲区(64kb或更大)增加吞吐量。您还可以转向异步处理,以便磁盘读取和计算可以重叠。

您可以使用此逻辑获取SHA-1值。 我在java中使用它

公共类sha1计算{

    public static void main(String[] args)throws Exception
    {
         File file = new File("D:\\Android Links.txt");
        String outputTxt= "";
        String hashcode = null;

        try {

            FileInputStream input = new FileInputStream(file);

            ByteArrayOutputStream output = new ByteArrayOutputStream ();
            byte [] buffer = new byte [65536];
            int l;

            while ((l = input.read (buffer)) > 0)
                output.write (buffer, 0, l);

            input.close ();
            output.close ();

            byte [] data = output.toByteArray ();


                MessageDigest digest = MessageDigest.getInstance( "SHA-1" ); 

            byte[] bytes = data;

            digest.update(bytes, 0, bytes.length);
            bytes = digest.digest();

            StringBuilder sb = new StringBuilder();

            for( byte b : bytes )
            {
                sb.append( String.format("%02X", b) );
            }

                System.out.println("Digest(in hex format):: " + sb.toString());


        }catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    }

SHA1管理不是大型输入字符串的最佳选择,Byte.ToString(“X2”)也不是将字节数组转换为字符串的最快方法

我刚刚完成了一篇关于该主题的详细基准测试的文章。文章比较了SHA1管理、SHA1 CryptoServiceProvider和SHA1Cng,还考虑了不同长度输入字符串上的SHA1.Create()

在第二部分中,它展示了将字节数组转换为字符串的5种不同方法,其中byte.ToString(“X2”)最差

我最大的输入只有10000个字符,所以你可能想在你的2GB文件上运行我的基准测试。如果这改变了数字,那将非常有趣


但是,对于文件完整性检查,您最好使用MD5,正如您已经编写的那样。

+1只是为了提高最重数据的性能,而不关心格式化是以一种相对低效的方式构建的:):)可能应该将其改为stringbuilder?啊,现在,您正在说服自己放弃+1!有什么值得呢但是,如果您经常生成这样的十六进制字符串,那么就需要有一个这样做的方法(对于扩展方法来说是一个很好的例子)。由于它被潜在地用于某个性能将产生更大影响的地方,因此移动StringBuilder(以适当的容量创建)会更有价值在60秒的中间,点击暂停按钮。它是在CopyTHASH还是在Str+++中?做几次。这会告诉你担心哪一个部分。如果是在CopyTHASH中,是从文件中获取缓冲区,还是在缓冲区中咀嚼?如果大部分是I/O绑定的话,如果大部分是I/O绑定的话。后者是CPU受限的。也许你可以朝着有时根本不需要散列的方向进行优化。例如,如果你对“两个文件是否不同”感兴趣,你可以先看看文件大小。但我正在扩展你提出的严格问题。我们需要更多的上下文。我已经运行了一些速度测试。