【问题标题】:Combine multiple maps into a map whose value for a given key is the sum of the values of the key in the combined maps将多个映射组合成一个映射,其给定键的值是组合映射中键的值的总和
【发布时间】:2019-03-31 10:55:49
【问题描述】:

我编写了一个程序,它可以识别文本文档中的所有唯一单词并计算每个单词出现的次数。为了提高我的程序的性能,我试图将单词计数分解为几个可以并行运行的 goroutine。

最初,我尝试使用通过引用传递给每个 goroutine 的单个映射,其中每个 goroutine 将计算文档部分中的单词。这引起了恐慌,因为程序试图同时从多个 goroutine 写入同一个映射。为了解决这个问题,我创建了一个互斥锁,可以防止多个 goroutine 同时写入映射。此时,程序按预期运行,但与 WordCount 函数的原始顺序实现相比没有性能差异。再想一想,这并不奇怪,因为互斥锁会强制其他 goroutine 在写入映射之前等待,从而阻止并行计算。

下面是使用互斥体来避免所描述的运行时恐慌的代码,但也无法并行计算字数。

func WordCount(words []string, startWord int, endWord int, freqs map[string]int, waitGroup *sync.WaitGroup, mutex *sync.Mutex) {
    mutex.Lock()
    for i := startWord; i < endWord; i++ {
        word := words[i]
        freqs[word]++
    }
    mutex.Unlock()
    waitGroup.Done()
}

func ParallelWordCount(text string) map[string]int {
    // Split text into string array of the words in text.
    text = strings.ToLower(text)
    text = strings.ReplaceAll(text, ",", "")
    text = strings.ReplaceAll(text, ".", "")
    words := strings.Fields(text)
    length := len(words)

    freqs := make(map[string]int)

    var mutex sync.Mutex
    var waitGroup sync.WaitGroup
    waitGroup.Add(2)
    defer waitGroup.Wait()

    threads := 2
    wordsPerThread := length / threads // always rounds down
    wordsInLastThread := length - (threads-1)*wordsPerThread
    startWord := -wordsPerThread
    var endWord int
    for i := 1; i <= threads; i++ {
        if i < threads {
            startWord += wordsPerThread * i
            endWord += wordsPerThread * i
        } else {
            startWord += wordsInLastThread
            endWord += wordsInLastThread
        }
        go WordCount(words, startWord, endWord, freqs, &waitGroup, &mutex)
    }

    return freqs
}

我相信,如果我为每个 goroutine 创建一个词频的本地图,并最终将本地频率图与整个文本文件的词数组合成一个图,我就可以实现并行字数统计。我目前面临的问题是如何组合本地频率图。具体来说,我需要知道如何将多个映射组合成一个映射,其给定键的值是要组合的映射中键的值的总和。

为了阐明我正在尝试做的事情的基本逻辑,我包含了以下示例。 ConcurrentSum 函数通过同时计算数组的下半部分和上半部分来返回数组中元素的总和。就我而言,我希望同时计算文本文件不同部分中的字数,并最终将字数组合成一个代表整个文档的字数图。

func sum(a []int, res chan<- int) {
    var sum int
    for i := 0; i < len(a); i++ {
        sum += a[i]
    }
    res <- sum
}

// concurrently sum the array a.
func ConcurrentSum(a []int) int {
    n := len(a)
    ch := make(chan int)
    go sum(a[:n/2], ch)
    go sum(a[n/2:], ch)
    return <-ch + <-ch
}

【问题讨论】:

    标签: go


    【解决方案1】:

    我相信您可以创建一组地图,每个地图都用于每个流程,然后使用列表读取每个地图以跟踪您已经计算过的单词。假设每个单词都是计算次数的关键,看起来就是这样。 考虑到并发方面,此处的并行处理可能不是最佳选择,因为所有内容都需要保持独立才能真正提高性能。如果您有存储空间,那么您当然可以使用列表并从地图的集成中获得最坏的 O(N) 效率。您需要将地图的集成保持在单个线程或单个进程中。

    【讨论】:

      猜你喜欢
      • 2020-05-13
      • 1970-01-01
      • 2018-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多