如何在Java中更快地计算SHA256?

11
我发现在Java中计算sha256的速度很慢,例如比Python慢。我编写了两个简单的基准测试,计算1GB零的sha256值。在两种情况下结果都是相同且正确的,但Python用时为5653毫秒,而Java用时为8623毫秒(慢53%)。每次结果都类似,这对我来说是一个重要的区别。
如何使Java中的计算更快?
基准测试:
Java:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class BenchmarkSha256 {

  public static void main(String... args) throws NoSuchAlgorithmException {
    int size = 1024 * 1024;
    byte[] bytes = new byte[size];
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    long startTime = System.nanoTime();
    for (int i = 0; i < 1024; i++)
      md.update(bytes, 0, size);
    long endTime = System.nanoTime();
    System.out.println(String.format("%1$064x", new java.math.BigInteger(1, md.digest())));
    System.out.println(String.format("%d ms", (endTime - startTime) / 1000000));
  }

}

Python:

-->

Python:

#!/usr/bin/env python

import hashlib
import time

size = 1024 * 1024
bytes = bytearray(size)
md = hashlib.sha256()
startTime = time.time()
for i in range(0, 1024):
  md.update(bytes)
endTime = time.time()
print "%s\n%d ms" % (md.hexdigest(), (endTime - startTime) * 1000)
结果:
~> java BenchmarkSha256
49bc20df15e412a64472421e13fe86ff1c5165e18b2afccf160d4dc19fe68a14
8623 ms

~> python BenchmarkSha256.py 
49bc20df15e412a64472421e13fe86ff1c5165e18b2afccf160d4dc19fe68a14
5653 ms

Java和Python的版本:

~> java -version
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)

~> python --version
Python 2.7

1
你是否在两种情况下排除了启动环境的成本,或者你对整个程序时间感兴趣而不是算法时间?(这两个都是值得问的问题,但它们是非常不同的指标。) - Donal Fellows
可能是Java: Calculate SHA-256 hash of large file efficiently的重复。 - Donal Fellows
@DonalFellows 我只关心算法时间,启动时间在这个规模下并不那么重要。我尝试在Java代码中注释掉“md.update(bytes, 0, size);”这一行,整个过程的时间为0.4秒,除了计算部分。即使减去这个时间,它仍然比较慢。 - Hristo Hristov
@DonalFellows 我认为这并不是重复的问题,因为在所引用的问题/回答中涉及了许多文件I/O操作,而我在这里只关心纯哈希计算。 - Hristo Hristov
1
我怀疑摘要使用的是Java而不是汇编库。您可以从Java中使用http://www.cryptopp.com/benchmarks.html。 - Peter Lawrey
4个回答

6

我对以下SHA-256实现进行了测试:Java内置、Groovy内置、Apache Commons、Guava和Bouncy Castle。我的一次测试结果在这里:

>groovy hash_comp.groovy
Hashing 1000000 iterations of SHA-256
time java: 2688         372023.8095238095 hashes/sec
time groovy: 1948       513347.0225872690 hashes/sec
time apache: 867        1153402.5374855825 hashes/sec
time guava: 953         1049317.9433368311 hashes/sec
time bouncy: 1890       529100.5291005291 hashes/sec

这是在Intel i5 8th Gen上运行的。Apache和Guava是最快的两个实现。Apache Commons在我的10次运行中以微弱优势击败了Guava。此测试的代码在此处可用。
请注意,在运行此测试后,我开始思考是否可以通过利用CPU指令集(Intel具有SHA扩展)来实现更快的速度。我不确定是否有一种JVM方法可以在没有JNI或JNA的情况下实现这一点。我创建了另一个问题在此处更新: 我发现的另一个选择是Amazon Corretto Crypto Provider (ACCP)。代码在此处可用。
“ACCP”是什么意思?
ACCP实现了标准Java加密架构(JCA)接口,并使用OpenSSL项目中提供的libcrypto替换了默认的Java加密实现。ACCP允许您充分利用汇编级别和CPU级别的性能优化,从而在多个服务和产品中获得显著的成本降低、延迟降低和更高的吞吐量,如下面的示例所示。

5

你尝试过逐步输入数据吗?你可以使用messageDigest.update()方法处理字节,然后使用messageDigest.digest()方法获取最终摘要。

在内存中分配1GB的数组是一个相当大的操作。最终你可能会发现较小的逐步更新最终更快。


我已经修改了Java版本,仅测量计算时间,结果基本相同。我将尝试增量更新变体并发布结果。 - Hristo Hristov
现在代码已经更新,实现了增量馈送和最终摘要,但问题仍然存在:Java 速度较慢。 - Hristo Hristov
我们应该给Java更多的机会,并让它进行JIT优化。或许运行代码数百次(在同一个VM启动中)来计时,将会显示一些差异。可能增加默认堆大小也会有所帮助(减少GC压力)?这个Python实现只是一个C库的薄包装层吗? - Jeff Foster
代码“md.update(...)”已经运行了1024次...我尝试增加堆大小-没有效果。我不知道Python实现,我只是采取了在两个平台上执行它的最直接方法。 - Hristo Hristov
@JeffFoster 是的,据我所知,Python使用了OpenSSL的薄包装器,因此我们基本上是在测试OpenSSL的C实现和Java之间的差异(Java可能没有使用JNI实现?) - Voo
@Voo 我们正在寻找更快的Java解决方案,基准测试只是为了比较一些东西。 - Hristo Hristov

2

除非您是为了比较两个命令行程序,否则这不是最好的测试。主要是因为每个程序相关的开销差异很大,这些数字被污染了。虚拟机启动时间会有所不同。内存分配速度也会有所不同。

要使其更清晰一些,只需在代码本身中每个实际MD5计算之前和之后进行两次时间采样。

这将实际测量哈希操作的性能。


谢谢,我已经做到了,现在Java版本只测量哈希计算的时间。问题已更新,仍然有效。 - Hristo Hristov
@Hristo:对于Python代码也要做同样的事情,否则还是不公平(但另一方面也不公平)。 - Donal Fellows
@allingeek 我应该指出你的“答案”实际上是一条评论。这应该是一条被点赞的评论,而不是一个答案。 - Hristo Hristov
@Hristo:现在你有了有效的数字,我说这是一个重复问题的原因告诉你“为什么”。 :-) - Donal Fellows
@DonalFellows 这些数字和预期的一样,那个问题并没有告诉我任何信息 :) - Hristo Hristov

-1

虽然您可能能够稍微提高Java工具的性能,但Python实现通常会更快,因为它很可能委托给组装库,这些库具有显着更好的性能。

如果您的项目没有任何其他重要的Java依赖项,我建议选择Python实现。


3
该项目只使用Java。Python实现仅用于演示。问题是如何更快地使用Java计算SHA256? - Hristo Hristov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接