Git如何快速计算SHA哈希值?

时间:2017-03-29 19:22:07

标签: java git scala hash sha

我知道git很快但我最近才知道它的速度有多快。

在我的一个项目中,我正在尝试计算一个巨大文件的SHA-256哈希值(82 MB,行数为850k),计算它需要一分多钟(包括散列和其他一些小操作)。

即使使用SHA-1,我花了30多秒,而git似乎只用了一两秒就完成了。

我通过组合文件的所有行,使用java的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会在ListmkString方法中包含文件的所有行,并返回列表中所有元素的字符串与给定的分隔符结合使用。

3 个答案:

答案 0 :(得分:7)

重新发布我之前的评论(扩展)。

你做的是:

  1. 读取字节
  2. 将它们转换为字符
  3. 将字符流拆分为行
  4. 将这些行存储到列表中
  5. 再次将这些行连接成一个字符串
  6. 再次取其字节
  7. 计算这些字节的哈希值
  8. 步骤2-6似乎没必要。我建议从块中的初始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
    

    至于sha1的性能,这里是time sha1sum <file>文件为179Mb的内容:

    real    0m0.607s
    user    0m0.588s
    sys 0m0.016s
    

答案 1 :(得分:1)

哈希计算在编译时被重定向到cache.h中的特定实现。底层平台可以提供优化的(例如,汇编器或机器相关的C编码)散列例程。当然,您的Java实现可能也可能不提供这样的例程。

如果平台没有自己的实现,Git provides one written in C适用于大内存块,并且仍然有一些手动调整和内联asm的架构和编译器ifdef

答案 2 :(得分:1)

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

我猜你导致慢的原因是你把它输入到列表而不是直接用它作为流。