首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java:高效计算大文件的SHA-256哈希值

Java:高效计算大文件的SHA-256哈希值
EN

Stack Overflow用户
提问于 2009-11-16 19:17:03
回答 7查看 29.6K关注 0票数 16

我需要计算一个大文件(或其中的一部分)的SHA-256散列。我的实现运行良好,但它比C++的CryptoPP计算慢得多(对于大约30 me的文件,我需要25分钟对10分钟)。我需要的是在C++和Java中相似的执行时间,所以哈希几乎在同一时间准备好。我也尝试了Bouncy Castle实现,但它给出了相同的结果。下面是我如何计算哈希:

代码语言:javascript
复制
int buff = 16384;
try {
    RandomAccessFile file = new RandomAccessFile("T:\\someLargeFile.m2v", "r");

    long startTime = System.nanoTime();
    MessageDigest hashSum = MessageDigest.getInstance("SHA-256");

    byte[] buffer = new byte[buff];
    byte[] partialHash = null;

    long read = 0;

    // calculate the hash of the hole file for the test
    long offset = file.length();
    int unitsize;
    while (read < offset) {
        unitsize = (int) (((offset - read) >= buff) ? buff : (offset - read));
        file.read(buffer, 0, unitsize);

        hashSum.update(buffer, 0, unitsize);

        read += unitsize;
    }

    file.close();
    partialHash = new byte[hashSum.getDigestLength()];
    partialHash = hashSum.digest();

    long endTime = System.nanoTime();

    System.out.println(endTime - startTime);

} catch (FileNotFoundException e) {
    e.printStackTrace();
}
EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2009-11-16 22:07:28

我的解释可能不能解决您的问题,因为它在很大程度上取决于您的实际运行时环境,但当我在我的系统上运行您的代码时,吞吐量受到磁盘I/O的限制,而不是哈希计算。这个问题并不是通过切换到NIO就能解决的,而仅仅是因为您正在以非常小的片段(16kB)读取文件。将我的系统上的缓冲区大小(buff)增加到1MB而不是16kB会使吞吐量增加一倍以上,但是在超过50MB/s的情况下,我仍然受到磁盘速度的限制,并且无法完全加载单个CPU核心。

顺便说一下:您可以通过将DigestInputStream包装在FileInputStream中,读取文件并从DigestInputStream中获取计算出的散列,而不是像代码中那样手动将数据从RandomAccessFile转移到MessageDigest,从而大大简化您的实现。

我用较旧的Java版本做了一些性能测试,这里似乎Java 5和Java 6之间存在相关的差异。不过,我不确定SHA实现是否经过了优化,或者VM执行代码的速度是否快了很多。我使用不同的Java版本(1MB缓冲区)得到的吞吐量是:

  • Sun JDK 1.5.0_15 (客户端):28MB/s,受限于CPU
  • Sun JDK 1.5.0_15 (服务器):45MB/s,受限于CPU
  • Sun JDK 1.6.0_16 (客户端):42MB/s,受限于CPU
  • Sun JDK 1.6.0_16 (服务器):52MB/s,受限于磁盘I/O (85-90%cpu load)

我对Opteron实现中汇编器部分的影响感到有点好奇,因为benchmarks results表明CryptoPP -256算法在Opteron实现上只需要15.8CPU周期/字节。不幸的是,我不能在cygwin上用gcc构建CryptoPP (构建成功,但生成的可执行文件立即失败),但在CryptoPP中使用和不支持汇编程序的情况下使用VS2005 (默认发布配置)构建了一个性能基准,并与内存缓冲区上的Java SHA实现进行了比较,省去了任何磁盘I/O,我在2.5 the的Phenom上得到了以下结果:

  • Sun JDK1.6.0_13 (服务器):26.2 cycles/byte
  • CryptoPP (仅限C++):21.8 cycles/byte
  • CryptoPP (汇编程序):13.3周期/字节

这两个基准测试都计算一个4 4GB空字节数组的SHA,在1MB的块中迭代它,这些块被传递给MessageDigest#update (Java)或CryptoPP的SHA256更新函数(C++)。

我能够在运行Linux的虚拟机上使用gcc 4.4.1 (-O3)构建CryptoPP并对其进行基准测试,结果只得到了appr。与VS exe的结果相比,吞吐量降低了一半。我不确定这种差异有多少是由虚拟机造成的,又有多少是由于VS通常比gcc生成更好的代码造成的,但我现在无法从gcc那里获得更准确的结果。

票数 37
EN

Stack Overflow用户

发布于 2009-11-16 19:33:54

也许今天的第一件事就是找出你在哪里花的时间最多?您是否可以通过分析器运行它,并查看花费时间最多的地方。

可能的改进:

哈希表使用NIO在单独的线程中读取fastest possible way

  • Update
  1. 中的文件。这实际上很难做到,也不适合胆小的人,因为它涉及到线程之间的安全发布。但是,如果您的性能分析显示在散列算法上花费了大量时间,则可能会更好地利用磁盘。
票数 4
EN

Stack Overflow用户

发布于 2009-11-16 19:36:17

我建议你使用像JProfiler这样的分析器或Netbeans (免费)中集成的分析器来找出时间实际花费在哪里,并专注于这一部分。

只是胡乱猜测--不确定它是否会有帮助--但你试过Server VM了吗?尝试使用java -server启动这个应用程序,看看这是否对你有帮助。服务器VM比默认的客户机VM更积极地将Java代码编译为本机代码。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1741545

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档