2

Goチュートリアルの一環として、複数のファイルにまたがる単語を数える簡単なプログラムを作成しています。ファイルを処理し、特定の単語の出現回数を通知するためのgoルーチンがいくつかあります。map[string]int次に、マップは縮小ルーチンに送信されます。このルーチンは、値を1つのマップに集約します。非常に単純に聞こえ、Go!の完璧な(map-reduce)タスクのように見えます。

私は160万のユニークな単語を含む約1万のドキュメントを持っています。私が見つけたのは、コードの実行中にメモリ使用量が急速かつ絶えず増加しており、処理の約半分(12GBボックス、7GB空き)でメモリが不足していることです。そうです、この小さなデータセットにはギガバイトを使用しています!

問題がどこにあるのかを理解しようとすると、データを収集して集約するレデューサーが原因であることがわかりました。コードは次のとおりです。

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に関しては直感が限られています。)それは望ましい動作ですか?私は何か間違ったことをしていますか?私は囲碁に失望するべきですか、それとも自分自身に失望するべきですか?

ありがとう!

4

1 に答える 1

2

ファイルを文字列に変換するコードはどのように見えますか?そこで問題を探します。大きなブロック(ファイル全体かもしれませんか?)を文字列に変換し、それらを単語にスライスする場合、1つの単語を保存すると、ブロック全体が固定されます。ブロックを[]byteのままにして、それらを単語にスライスしてから、単語を個別に文字列タイプに変換してみてください。

于 2012-04-29T23:13:02.033 に答える