【问题标题】:What is the less expensive hash algorithm?什么是更便宜的哈希算法?
【发布时间】:2013-10-03 21:28:31
【问题描述】:

我对哈希算法了解不多。

在将文件转发到需要 MD2/MD5/SHA-X 中的文件哈希的远程系统(有点像 S3)之前,我需要在 Java 中计算传入文件的哈希值。 此哈希不是出于安全原因计算的,而只是为了一致性校验和。

我能够在转发文件时使用 Java 标准库的 DigestInputStream 实时计算此哈希值,但想知道哪种算法最适合避免使用 DigestInputStream 时的性能问题?

我的一位前同事测试并告诉我们,与 unix 命令行或文件相比,实时计算哈希可能非常昂贵。


修改过早优化: 我在一家公司工作,该公司旨在帮助其他公司将他们的文件非物质化。 这意味着我们有一批处理来自其他公司的文件传输。我们的目标是未来每天数百万个文档,实际上,这批的执行时间对我们的业务很敏感。

每天对 100 万个文档进行 10 毫秒的哈希优化,每天的执行时间减少了 3 小时,这是相当巨大的。

【问题讨论】:

  • 您应该能够在一台使用单核的不错的机器上散列超过 100MB/s,因此除非您使用的是千兆互联网,否则它不应该成为真正的瓶颈。
  • 过早的优化是万恶之源。我绝对认为您应该选择一个在技术上足以满足您的目标的哈希,如果它证明存在性能问题,请相应地进行更改...
  • 如果您真的不需要安全性,那么 MD5 是一个不错的选择。但是,如果您能承受性能损失,请使用 SHA-2(SHA-256 或 SHA-512)
  • @CodesInChaos 我尝试在 80mb 文件上使用 MessageDigest,但消耗 InputStream 似乎要多花 300 毫秒。
  • @SebastienLorber 有了这个数字(260MB/s),只有当你有 2Gb/s 的网络连接时,散列才会限制你。如果这确实是一个限制,您可以切换到本机代码。原生 MD5 应该在 500 到 1000 MB/s 之间。

标签: java encryption hash


【解决方案1】:

如果您只是想检测传输过程中的意外损坏等,那么一个简单的(非加密)校验和就足够了。但请注意(例如)16 位校验和将无法在 216 中检测到一次随机损坏。并且它无法防止有人故意修改数据。

Checksums 上的 Wikipedia 页面列出了各种选项,包括一些常用(且便宜)的选项,例如 Adler-32 和 CRC。

但是,我同意@ppeterka。这有点“过早优化”的味道。

【讨论】:

    【解决方案2】:

    我知道很多人不相信微基准,但让我发布我所得到的结果。

    输入:

    bigFile.txt = appx 143MB size

    hashAlgorithm = MD2, MD5, SHA-1

    测试代码:

           while (true){
                long l = System.currentTimeMillis();
                MessageDigest md = MessageDigest.getInstance(hashAlgorithm);
                try (InputStream is = new BufferedInputStream(Files.newInputStream(Paths.get("bigFile.txt")))) {
                    DigestInputStream dis = new DigestInputStream(is, md);
                    int b;
                    while ((b = dis.read()) != -1){
                    }
                }
                byte[] digest = md.digest();
                System.out.println(System.currentTimeMillis() - l);
            }
    

    结果:

    MD5
    ------
    22030
    10356
    9434
    9310
    11332
    9976
    9575
    16076
    -----
    
    SHA-1
    -----
    18379
    10139
    10049
    10071
    10894
    10635
    11346
    10342
    10117
    9930
    -----
    
    MD2
    -----
    45290
    34232
    34601
    34319
    -----
    

    似乎MD2MD5SHA-1 慢一些

    【讨论】:

    • 谢谢,但逐字节读取会导致性能不佳。我可以在没有哈希的情况下在 200 毫秒内读取该文件,使用 MD5 可以在 300 毫秒内读取该文件,这似乎给出了最好的结果
    • 然而 MD2、MD5、SHA-1 或任何加密校验和都不适合这项工作。您正在微基准测试中测量自卸卡车的加速度,以确定是否适合参加 Indy 赛车比赛。
    • @GregS 你能解释一下,你是什么意思吗?
    • @SebastienLorber:您的问题表明您希望检测意外的文件损坏而不是故意的文件操作。像 Adler-32 或 CRC(参见 Stephen C 的回答)这样的校验和比 MD-x 或 SHA-x 更快、更合适。
    • 实际上是我们将文件发送到的远程主机进行哈希检查(我认为这在法国非物质化规范中是合法的)并且不支持校验和算法
    【解决方案3】:

    像 NKukhar 一样,我尝试做一个微基准测试,但使用不同的代码和更好的结果:

    public static void main(String[] args) throws Exception {
        String bigFile = "100mbfile";
    
    
        // We put the file bytes in memory, we don't want to mesure the time it takes to read from the disk
        byte[] bigArray = IOUtils.toByteArray(Files.newInputStream(Paths.get(bigFile)));
        byte[] buffer = new byte[50_000]; // the byte buffer we will use to consume the stream
    
        // we prepare the algos to test
        Set<String> algos = ImmutableSet.of(
                "no_hash", // no hashing
                MessageDigestAlgorithms.MD5,
                MessageDigestAlgorithms.SHA_1,
                MessageDigestAlgorithms.SHA_256,
                MessageDigestAlgorithms.SHA_384,
                MessageDigestAlgorithms.SHA_512
        );
    
        int executionNumber = 20;
    
        for ( String algo : algos ) {
          long totalExecutionDuration = 0;
          for ( int i = 0 ; i < 20 ; i++ ) {
            long beforeTime = System.currentTimeMillis();
            InputStream is = new ByteArrayInputStream(bigArray);
            if ( !"no_hash".equals(algo) ) {
              is = new DigestInputStream(is, MessageDigest.getInstance(algo));
            }
            while ((is.read(buffer)) != -1) {  }
            long executionDuration = System.currentTimeMillis() - beforeTime;
            totalExecutionDuration += executionDuration;
          }
          System.out.println(algo + " -> average of " + totalExecutionDuration/executionNumber + " millies per execution");
        }
      }
    

    这会在一台好的 i7 开发者机器上为一个 100mb 的文件生成以下输出:

    no_hash -> average of 6 millies per execution
    MD5 -> average of 201 millies per execution
    SHA-1 -> average of 335 millies per execution
    SHA-256 -> average of 576 millies per execution
    SHA-384 -> average of 481 millies per execution
    SHA-512 -> average of 464 millies per execution
    

    【讨论】:

    • 也用“CRC32”做一个测试。
    猜你喜欢
    • 2021-02-09
    • 2012-11-21
    • 1970-01-01
    • 2011-06-21
    • 1970-01-01
    • 2013-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多