作为我的 Go 教程的一部分,我正在编写简单的程序来计算多个文件中的单词数。我有一些 go 例程用于处理文件并创建 map[string]int
来告诉已找到特定单词的出现次数。然后,该映射被发送到减少例程,该例程将值聚合到单个映射。听起来非常简单,看起来像是 Go 的完美(map-reduce)任务!
我有大约 10k 文档,其中包含 160 万个唯一单词。我发现,在运行代码时,我的内存使用量不断快速增长,并且在处理过程的一半时内存就耗尽了(12GB 盒子,7GB 可用空间)。所以是的,这个小数据集使用千兆字节!
试图找出问题所在,我发现归咎于收集和聚合数据的 reducer 。代码如下:
func reduceWords (input chan map[string]int, output chan int) {
total := make(map[string]int)
for wordMap := range input {
for w, c := range wordMap {
total[w] += c
}
}
output <- len(total)
}
如果我从上面的示例中删除 map ,内存将保持在合理的限制内(几百兆字节)。但我发现,复制字符串也可以解决问题,即以下示例不会耗尽我的内存:
func reduceWords (input chan map[string]int, output chan int) {
total := make(map[string]int)
for wordMap := range input {
for w, c := range wordMap {
copyW := make([]byte, len(w)) // <-- will put a copy here!
copy(copyW, w)
total[string(copyW)] += c
}
}
output <- len(total)
}
当我直接使用该值时,是否有可能在每次迭代后都没有破坏 wordMap
实例? (作为一名 C++ 程序员,我对 GC 的直觉有限。)这是理想的行为吗?难道我做错了什么?我应该对 Go 失望还是对自己失望?
谢谢!
最佳答案
将文件转换为字符串的代码是什么样的?我会在那里寻找问题。如果您要将大块(可能是整个文件?)转换为字符串,然后将它们 slice 为单词,那么如果您保存任何一个单词,则您将固定整个 block 。尝试将 block 保留为 []byte,将它们 slice 为单词,然后将单词单独转换为字符串类型。
关于memory - Go:重用映射键时会占用大量内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10376755/