作为我的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)
}
如果我从上面的示例中删除地图,内存将保持在合理的范围内(几百兆字节)。我发现,复制一个字符串也解决了这个问题,即以下示例不会占用我的内存:
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感到失望还是对自己感到失望?
谢谢!