【问题标题】:Hash large file using little memory使用很少的内存散列大文件
【发布时间】:2021-11-16 00:40:22
【问题描述】:

我需要散列非常大的文件(>10TB 文件)。所以我决定每 MB 散列 128KB。 我的想法是将文件分成 1MB 的块,并且只对每个块的前 128KB 进行哈希处理。

以下代码有效,但它使用了大量的内存,我不知道为什么......

func partialMD5Hash(filePath string) string {
    var blockSize int64 = 1024 * 1024
    var sampleSize int64 = 1024 * 128

    file, err := os.Open(filePath)
    if err != nil {
        return "ERROR"
    }
    defer file.Close()
    fileInfo, _ := file.Stat()
    fileSize := fileInfo.Size()

    hash := md5.New()

    var i int64
    for i = 0; i < fileSize / blockSize; i++ {
        sample := make([]byte, sampleSize)
        _, err = file.Read(sample)
        if err != nil {
            return "ERROR"
        }
        hash.Write(sample)

        _, err := file.Seek(blockSize-sampleSize, 1)
        if err != nil {
            return "ERROR"
        }
    }

    return hex.EncodeToString(hash.Sum(nil))
}

任何帮助将不胜感激!

【问题讨论】:

  • 您在堆上分配了那么多数据。使 GC 混乱。减慢你的进程。吃掉你的记忆。
  • @mh-cbon 我该如何避免呢?
  • 查看 Burak Serdar 的回答。
  • @mh-cbon 如果您阅读 cmets,您会知道他的帖子不是答案。尽管我将样本从循环中取出,但它仍然表现出相同的行为。
  • 考虑一下,也许这不是问题的根源。

标签: go memory hash memory-leaks


【解决方案1】:

这种方法和程序存在几个问题。

如果你想散列一个大文件,你必须散列所有文件。对文件的某些部分进行采样不会检测到对您未采样的部分的修改。

您为每次迭代分配一个新缓冲区。相反,在 for 循环之外分配一个缓冲区,然后重用它。

此外,您似乎忽略了实际读取的字节数。所以:

    block := make([]byte, blockSize)
    for {
        n, err = file.Read(block)
        if n>0 {
           hash.Write(sample[:n])
        }
        if err==io.EOF {
           break
        }
        if err != nil {
            return "ERROR"
        }
    }

但是,以下内容会更简洁:

io.Copy(hash,file)

【讨论】:

  • 嗨,我只是对文件的一部分进行哈希处理,因为对于这种类型的文件,任何更改都会影响至少几 GB。因此,每 1mb 无法捕获 128kb 的机会非常小。即使是这样,它也不是绝对关键的。我错过了缓冲区,现在将它移到外面,但它仍在使用大量内存。编辑:另外,当您说“另外,您似乎忽略了实际读取的字节数”时,不确定您的意思。
  • 你忽略了Read的返回值
  • 定义“大量内存”
  • >30GB 内存
  • 如果这就是您的程序所做的全部,并且如果您将缓冲区分配移到 for 循环之外,那么此代码将不会使用那么多内存。我不知道任务管理器显示什么度量值。
猜你喜欢
  • 1970-01-01
  • 2013-02-06
  • 1970-01-01
  • 2021-04-05
  • 1970-01-01
  • 1970-01-01
  • 2014-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多