2

作为我的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感到失望还是对自己感到失望?

谢谢!

4

1 回答 1

2

您的代码是什么样的,可以将文件转换为字符串?我会在那里寻找问题。如果您要将大块(可能是整个文件?)转换为字符串,然后将它们切成单词,那么如果您保存任何一个单词,您就会固定整个块。尝试将块保留为 []byte,将它们切成单词,然后将单词单独转换为字符串类型。

于 2012-04-29T23:13:02.033 回答