代码之家  ›  专栏  ›  技术社区  ›  Bruce Adams

提高sha-1计算哈希的性能

  •  6
  • Bruce Adams  · 技术社区  · 14 年前

    我正在使用以下代码对一个工作正常的文件进行校验和检查。但是,当我为一个大文件(比如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();
    

    更新:

    系统:

    操作系统:win 7 64位,CPU:i5 750,RAM:4GB,HDD:7200rpm

    测验:

    测试1=59.895秒

    测试2=59.94秒

    6 回复  |  直到 7 年前
        1
  •  3
  •   Jon Hanna    7 年前

    第一个问题是您需要这个校验和来做什么。如果不需要加密属性,那么非加密散列或加密安全性较低的散列(MD5被“破坏”不会阻止它成为一个好的散列,也不会对某些用途仍然足够强)可能更具性能。您可以通过读取数据的一个子集来创建自己的哈希(我建议让这个子集在底层文件的4096字节块中工作,因为这将与sha1管理使用的缓冲区大小相匹配,并且允许比为某个值每x字节读取一个更快的块。X的)。

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

    当然,如果您希望文件的sha-1与其他内容进行互操作,那么您就陷入了困境。

    我将尝试使用不同的缓冲区大小,因为增加filestream缓冲区的大小可以提高速度,但需要额外的内存。我建议使用4096的整数倍(4096是默认值,顺便说一下),因为sha1managed会一次请求4096个块,这样一来,在任何情况下,都不会出现filestream返回小于最大请求值(允许但有时是次优)或一次执行多个副本的情况。

        2
  •  1
  •   Jon Skeet    14 年前

    是IO绑定还是CPU绑定?如果是CPU限制的话,我们就没什么可以做的了。

    有可能打开 FileStream 使用不同的参数将允许文件系统进行更多的缓冲,或者假定您将按顺序读取文件-但我怀疑这将有助于 非常 很多。(如果是CPU限制的话,它肯定不会做很多事情。)

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

    如果您有大量的内存(例如4GB或更多内存),那么第二次散列文件需要多长时间,文件系统缓存中可能会出现这种情况?

        3
  •  1
  •   schnaader    14 年前

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

    为了加快速度,您可能不只是散列整个文件,而是散列文件的前x kb或可表示部分(最可能不同的部分)。如果您的文件不太相似,这不应该导致重复。

        4
  •  1
  •   snemarch    14 年前

    首先:sha-1文件散列应该绑定在非古CPU上的I/O,而i5当然不符合古CPU的条件。当然,这取决于sha-1的实现,但我怀疑sha 1管理是否缓慢。

    接下来,2GB数据的60秒是~34MB/s——硬盘读取速度很慢;即使是2.5英寸的笔记本电脑磁盘也可以读取速度比这快。假设硬盘驱动器是内部的(没有USB2/无论什么或网络瓶颈),并且没有很多其他磁盘I/O活动,我会惊讶地看到从现代驱动器读取的数据少于60MB/s。

    我的 猜测 会是那样吗 ComputeHash() 在内部使用小缓冲区。尝试手动读取/散列,这样可以指定更大的缓冲区(64kb或更大)以增加吞吐量。您还可以移动到异步处理,以便磁盘读取和计算可以重叠。

        5
  •  0
  •   Wintermute    10 年前

    对于大型输入字符串,sha1管理也不是最佳选择,byte.toString(“x2”)也不是将字节数组转换为字符串的最快方法。

    我刚刚完成了一篇关于这个主题的详细基准的文章。它比较了sha1Managed、sha1CryptoServiceProvider和sha1Cng,还考虑在不同长度的输入字符串上使用sha1.create()。

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

    我最大的输入只有10000个字符,所以您可能希望在2GB文件上运行我的基准测试。如果这改变了数字会很有趣。

    http://wintermute79.wordpress.com/2014/10/10/c-sha-1-benchmark/

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

        6
  •  -1
  •   Nayan Rath    11 年前

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

    公共类1计算{

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