我需要计算一个大文件(或其中的一部分)的SHA-256散列。我的实现运行良好,但它比C++的CryptoPP计算慢得多(对于大约30 me的文件,我需要25分钟对10分钟)。我需要的是在C++和Java中相似的执行时间,所以哈希几乎在同一时间准备好。我也尝试了Bouncy Castle实现,但它给出了相同的结果。下面是我如何计算哈希:
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();
}发布于 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缓冲区)得到的吞吐量是:
我对Opteron实现中汇编器部分的影响感到有点好奇,因为benchmarks results表明CryptoPP -256算法在Opteron实现上只需要15.8CPU周期/字节。不幸的是,我不能在cygwin上用gcc构建CryptoPP (构建成功,但生成的可执行文件立即失败),但在CryptoPP中使用和不支持汇编程序的情况下使用VS2005 (默认发布配置)构建了一个性能基准,并与内存缓冲区上的Java SHA实现进行了比较,省去了任何磁盘I/O,我在2.5 the的Phenom上得到了以下结果:
这两个基准测试都计算一个4 4GB空字节数组的SHA,在1MB的块中迭代它,这些块被传递给MessageDigest#update (Java)或CryptoPP的SHA256更新函数(C++)。
我能够在运行Linux的虚拟机上使用gcc 4.4.1 (-O3)构建CryptoPP并对其进行基准测试,结果只得到了appr。与VS exe的结果相比,吞吐量降低了一半。我不确定这种差异有多少是由虚拟机造成的,又有多少是由于VS通常比gcc生成更好的代码造成的,但我现在无法从gcc那里获得更准确的结果。
发布于 2009-11-16 19:33:54
也许今天的第一件事就是找出你在哪里花的时间最多?您是否可以通过分析器运行它,并查看花费时间最多的地方。
可能的改进:
哈希表使用NIO在单独的线程中读取fastest possible way
发布于 2009-11-16 19:36:17
我建议你使用像JProfiler这样的分析器或Netbeans (免费)中集成的分析器来找出时间实际花费在哪里,并专注于这一部分。
只是胡乱猜测--不确定它是否会有帮助--但你试过Server VM了吗?尝试使用java -server启动这个应用程序,看看这是否对你有帮助。服务器VM比默认的客户机VM更积极地将Java代码编译为本机代码。
https://stackoverflow.com/questions/1741545
复制相似问题