Java Git是如何如此快速地计算SHA哈希的?
我知道git很快,但直到最近我才发现它真的可以有多快 在我的一个项目中,我试图计算一个大文件的SHA-256散列(82MB,850k行),计算它花费了一分钟(包括散列和其他一些小操作) 即使使用SHA-1,我也花了30多秒的时间,而git似乎只用了一两秒钟就完成了 我正在使用java的Java Git是如何如此快速地计算SHA哈希的?,java,git,scala,hash,sha,Java,Git,Scala,Hash,Sha,我知道git很快,但直到最近我才发现它真的可以有多快 在我的一个项目中,我试图计算一个大文件的SHA-256散列(82MB,850k行),计算它花费了一分钟(包括散列和其他一些小操作) 即使使用SHA-1,我也花了30多秒的时间,而git似乎只用了一两秒钟就完成了 我正在使用java的SecurityAPI结合文件的所有行来计算Scala中的哈希 val lines = Source.fromFile(filePath, "UTF-8").getLines().toList MessageDig
Security
API结合文件的所有行来计算Scala中的哈希
val lines = Source.fromFile(filePath, "UTF-8").getLines().toList
MessageDigest.getInstance("SHA-256")
.digest(lines.mkString("\n").getBytes).map("%02x".format(_)).mkString
那么,Git是如何做到这么快的,或者更重要的问题是,为什么我的方法这么慢
编辑:对于那些不熟悉scala语法的人,
lines
将把文件的所有行都放在一个List
中,并且mkString
方法返回一个包含列表中所有元素的字符串,该字符串与给定的分隔符组合在一起 哈希计算在编译时重定向到中的特定实现。底层平台可提供优化(例如,汇编程序或机器相关的C编码)散列例程。当然,您的Java实现可能也可能不提供这样的例程
如果平台没有自己的实现,Git可以在大内存块上工作,并且仍然有一些手动调整,并使用架构和编译器
ifdef
s内联asm
s.重新发布我之前的评论(扩展)
你要做的是:
FileInputStream
中分块(例如,4k)读取字节,并将它们提供给MessageDigest
进行更新。这将只执行步骤1和7
InputStream is = new FileInputStream(fileName);
byte[] buffer = new byte[4096];
while (true) {
int read = is.read(buffer);
if (read < 0) {
break;
}
md.update(buffer, 0, read);
}
is.close(); // better be done in finally
Git无疑要快一些,但SHA-1的30秒就没那么好了 所以我用java运行了一个测试:
public static void main(String[] args) throws Exception{
long startTime = System.currentTimeMillis();
byte[] bytes = createSha1(new File("src\\main\\resources\\200mb_file.zip"));
System.out.println(new String(bytes));
long endTime = System.currentTimeMillis();
long duration = (endTime - startTime);
System.out.format("Duration: %dms\n", duration);
}
private static byte[] createSha1(File file) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
InputStream fis = new FileInputStream(file);
int n = 0;
byte[] buffer = new byte[8192];
while (n != -1) {
n = fis.read(buffer);
if (n > 0) {
digest.update(buffer, 0, n);
}
}
return digest.digest();
}
输出:
Duration: 1531
我猜是什么原因导致您的速度慢,是因为您正在将其输入列表,而不是直接将其用作流。您检查了Git的源代码吗?那应该是开始的地方。@JimGarrison,我试着去寻找它,但我没有找到实际进行哈希运算的确切代码。另外,我对C代码不是很熟悉,我不认为我能很好地理解它。。。很多额外的工作。难道您没有尝试直接从
InputStream
(以4k块为单位)读取字节,并将它们提供给MessageDigest
实例进行更新吗?这可能要快得多。按照@RomanPuchkovskiy的建议读取原始字节可能更接近Git的做法。Git不关心行,它将散列文本、二进制,这对Git来说都是一样的。计算差异时,它只关心线条。@RomanPuchkovskiy。我之所以把它列入清单是因为我需要在中间的另一个操作。但是,这种解析是否会让它变得如此缓慢?我将尝试你建议的方法,并将看到性能。看起来额外的过程使它慢了很多。我试着像你建议的那样读取字节并将它们交给hasher,现在大约需要30秒,以前大约是65秒。虽然速度是原来的两倍,但30秒似乎还是很长的时间,尤其是当Git能够以如此快的速度完成任务时。我会继续寻找更好的方法,谢谢你的回答。这很奇怪,我尝试在同一个179Mb文件上用上面的代码计算sha1哈希,结果花费了real 0m1.192s
。比sha1sum慢两倍,但不是一个数量级。我已经多次重复这两个测试,让系统缓存等等。一些探查器可能会帮助您找出问题。一个有趣的事实:清空FS缓冲区+缓存后,java程序会更快:它需要1.4秒,而sha1sum需要1.7秒。哇。比本地性能更快,这很有趣!
Duration: 1531