2013-08-25 17 views
2

最近我正在爲go中的文件創建校驗和。我的代碼正在處理小文件和大文件。我嘗試了兩種方法,第一種使用ioutil.ReadFile("filename"),第二種使用os.Open("filename")文件讀取和校驗和進去。方法之間的區別

例子:

第一個功能正在與io/ioutil和適用於小文件。當我嘗試複製一個大文件時,我的RAM獲得了blastt,對於1.5GB的iso,它使用3GB的RAM。

func byteCopy(fileToCopy string) { 
    file, err := ioutil.ReadFile(fileToCopy) //1.5GB file 
    omg(err)         //error handling function 
    ioutil.WriteFile("2.iso", file, 0777) 
    os.Remove("2.iso") 
} 

即使當我想創建一個校驗和crypto/sha512io/ioutil更糟。 它將永遠不會完成並放棄,因爲它耗盡內存。

func ioutilHash() { 
    file, _ := ioutil.ReadFile(iso) 
    h := sha512.New() 
    fmt.Printf("%x", h.Sum(file)) 
} 

當使用下面的函數時,一切正常。

func ioHash() { 
    f, err := os.Open(iso) //iso is a big ~ 1.5tb file 
    omg(err)    //error handling function 
    defer f.Close() 
    h := sha512.New() 
    io.Copy(h, f) 
    fmt.Printf("%x", h.Sum(nil)) 
} 

我的問題:

爲什麼不工作的ioutil.ReadFile()功能吧? 1.5GB文件不應該填充我的16GB內存。我不知道現在在哪裏看。 有人可以解釋這些方法之間的差異嗎?閱讀go-doc和示例並不能理解它。 有可用的代碼是很好的,但理解爲什麼它的工作就是這樣。

在此先感謝!

回答

3

下面的代碼不會做你認爲它的作用。

func ioutilHash() { 
    file, _ := ioutil.ReadFile(iso) 
    h := sha512.New() 
    fmt.Printf("%x", h.Sum(file)) 
} 

這首先讀取您的1.5GB iso。正如jnml指出的那樣,它不斷地做出越來越大的緩衝區來填充它。最後,總緩衝區大小不小於1.5GB,不大於1.875GB(按目前的實施)。

但是,之後,你再製造一個緩衝區! h.Sum(file)不散列文件。它將當前散列附加到文件!這可能會或可能不會導致另一個分配。

真正的問題是您正在接受該文件,現在附加了散列,並使用%x打印。Fmt實際上使用相同類型的方法jnml預先計算,指出使用了ioutil.ReadAll。所以它不斷分配更大更大的緩衝區來存儲文件的十六進制。由於每個字母都是4位,這意味着我們正在談論不少於3GB的緩衝區,並且不超過3.75GB。

這意味着您的活動緩衝區可能會很大,爲5.625GB。將GC與GC結合並不完美,不要刪除所有中間緩衝區,它可以很容易地填滿你的空間。


編寫該代碼的正確方法應該是。

func ioutilHash() { 
    file, _ := ioutil.ReadFile(iso) 
    h := sha512.New() 
    h.Write(file) 
    fmt.Printf("%x", h.Sum(nil)) 
} 

這不會做幾乎分配的數量。


底線是ReadFile很少是你想使用的。 IO流媒體(使用讀者和作者)始終是最佳選擇。當你使用io.Copy時,你不僅分配少得多,還可以同時散列和讀取磁盤。在您的ReadFile示例中,兩個資源在不相互依賴時同步使用。

1

ioutil.ReadFile正在工作。通過使用該函數來濫用系統資源是你的錯,因爲你知道這些資源是巨大的。

ioutil.ReadFile是一個方便的助手,你很確定的文件他們會很小。 (其實它是爲files <= 1e9 bytes優化的東西,但這是一個實現細節,而不是API合同的一部分。你的1.5GB文件迫使它使用片增長並因此分配多個在讀取文件的過程中緩衝您的數據。)

即使您使用os.File的其他方法也不行。您絕對應該使用「bufio」包來順序處理大文件,請參閱bufio.NewReader

相關問題