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