2

最近我正在为 go 中的文件创建校验和。我的代码正在处理大小文件。我尝试了两种方法,第一种使用ioutil.ReadFile("filename"),第二种使用os.Open("filename").

例子:

第一个功能是处理io/ioutil小文件。当我尝试复制一个大文件时,我的 ram 被爆破,对于 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")
}

更糟糕的是,当我想用​​ and 创建校验crypto/sha512和时io/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 和示例时,我不明白。拥有可用的代码很好,但要理解为什么它的工作方式远高于此。

提前致谢!

4

2 回答 2

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 并不完美,也没有删除所有中间缓冲区,它很容易填满你的空间。


编写该代码的正确方法是。

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

这几乎没有完成分配的数量。


底线是 ReadFile 很少是您想要使用的。IO 流式传输(使用读取器和写入器)始终是最好的选择。使用 io.Copy 时不仅分配更少,还可以同时散列和读取磁盘。在您的 ReadFile 示例中,当两个资源不相互依赖时,它们会同步使用。

于 2013-08-25T16:36:51.603 回答
1

ioutil.ReadFile工作正常。通过使用该功能来滥用系统资源是您的错,因为您知道这是巨大的。

ioutil.ReadFile是您事先确定它们会很小的文件的方便帮手。像配置文件,大多数源代码文件等(实际上它正在优化文件 <= 1e9 字节的东西,但这是一个实现细节,而不是 API 合同的一部分。你的 1.5GB 文件迫使它使用切片增长,因此分配超过在读取文件的过程中为您的数据提供一个大缓冲区。)

即使您使用的其他方法os.File也不行。您绝对应该使用“bufio”包来顺序处理大文件,请参阅bufio.NewReader.

于 2013-08-25T16:16:45.257 回答