2017-03-29 131 views
0

我知道git速度很快,但我最近才發現它的速度有多快。Git如何快速計算SHA散列?

在我的其中一個項目中,我試圖計算一個巨大文件(82 MB和850k行)的SHA-256散列,並花了一分鐘計算它(包括散列和其他一些小操作) 。

即使使用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如何做到這麼快,或者說更重要的問題,爲什麼我的方法如此之慢?

編輯:對於那些不熟悉斯卡拉語法,lines將文件的所有行的ListmkString方法返回列表中與給定的分離器結合所有元素的字符串。

+0

您是否檢查過Git的源代碼?那將是開始的地方。 –

+0

@JimGarrison,我試圖尋找它,但我沒有找到實際做哈希的確切代碼。另外我對C代碼不是很熟悉,我不認爲我能夠理解它。 –

+6

解析,把它放到一個數據結構(列表)中,合併,再次獲取字節......很多額外的工作。你不是試圖直接從你的'InputStream'(4k塊)讀取字節,並將它們送到MessageDigest實例進行更新嗎?這可能會更快。 –

回答

7

轉發我之前的評論(擴展)。

你要做的就是:

  1. 讀取的字節
  2. 將它們轉換成字符
  3. 拆分字符流線
  4. 存儲這些行到一個列表
  5. 再次串連這些行成一個字符串
  6. 再次取其字節
  7. Comp這些字節的排序散列

步驟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>其中file是179Mb:

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

看起來額外的過程讓它慢了很多。我試着只讀取字節並按照你所建議的方式將它們提供給哈希,現在它花費了大約30秒,之前約爲65秒。儘管速度提高了一倍,但30秒仍然像是很多時間,特別是當Git能夠快速完成時。我會繼續尋找更好的方法,並感謝您的答案。 –

+0

這很奇怪,我試着在同一個179Mb文件上用上面的代碼計算sha1哈希值,並且它花了'real \t 0m1.192s'。比sha1sum慢兩倍,但不是一個數量級。我重複了兩次測試以讓系統緩存等等。某些分析器可能會幫助您查明問題。 –

+0

一個有趣的事實:清空FS緩衝區+緩存後http://unix.stackexchange.com/questions/87908/how-do-you-empty-the-buffers-and-cache-on-a-linux-system java程序**更快**:需要1.4秒,而sha1sum需要1.7秒。 –

1

哈希計算在編譯時被重定向到cache.h中的特定實現。底層平臺可以提供優化的(例如,彙編程序或機器相關的C編碼的)散列程序。當然,你的Java實現可能也可能不提供這樣的例程。

如果該平臺沒有自己的實現,Git provides one written in C可以在大內存塊上工作,並且仍然有一些手工調整,並且內聯asm的體系結構和編譯器ifdef s。

1

的Git無疑是快,但對於SHA30秒-1不是很好。

於是我就在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 

我的猜測是什麼原因造成的緩慢是您它直接輸入到一個列表,而事實上用它作爲一個流。