3

我对哈希算法了解不多。

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

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

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


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

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

4

3 回答 3

5

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

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

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

于 2013-10-03T11:10:47.150 回答
1

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

输入:

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
-----

似乎MD2有点慢,MD5或者SHA-1

于 2013-10-03T11:41:54.623 回答
1

Like NKukhar I've tried to do a micro-benchmark, but with a different code and better results:

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

This produces the following output for a 100mb file on a good i7 developer machine:

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
于 2013-10-03T13:28:31.080 回答