Объединить несколько карт в карту, значение которой для данного ключа равно сумме значений ключа в объединенных картах

#go

#Вперед

Вопрос:

Я написал программу, которая идентифицирует все уникальные слова в текстовом документе и подсчитывает, сколько раз встречается каждое из слов. Чтобы повысить производительность моей программы, я пытаюсь разбить подсчет слов на несколько подпрограмм, которые могут выполняться параллельно.

Изначально я пытался использовать единственную карту, которая передавалась по ссылке на каждую подпрограмму, где каждая подпрограмма считала бы слова в части документа. Это вызвало панику, потому что программа пыталась выполнить запись на одну и ту же карту из нескольких подпрограмм одновременно. Чтобы решить эту проблему, я создал мьютекс, который предотвратил бы одновременную запись нескольких подпрограмм на карту. На этом этапе программа функционировала как ожидалось, но без разницы в производительности по сравнению с исходной последовательной реализацией WordCount функции. Если подумать, это неудивительно, учитывая, что мьютекс заставляет другие подпрограммы ждать перед записью в карту, следовательно, предотвращая параллельные вычисления.

Ниже приведен код, который использует мьютекс, чтобы избежать описанной паники во время выполнения, но также не позволяет параллельно считать слова.

 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, amp;waitGroup, amp;mutex)
    }

    return freqs
}
  

Я полагаю, что мог бы добиться параллельного подсчета слов, если бы создал локальную карту частот слов для каждой подпрограммы и, в конце концов, объединил карты локальных частот в единую карту с количеством слов для всего текстового файла. Проблема, с которой я в настоящее время сталкиваюсь, заключается в том, как объединить карты локальных частот. Конкретно, мне нужно знать, как объединить несколько карт в карту, значение которой для данного ключа равно сумме значений ключа на картах, которые будут объединены.

Чтобы прояснить основную логику того, что я пытаюсь сделать, я включил приведенный ниже пример. 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
}
  

Ответ №1:

Я полагаю, вы могли бы создать массив карт, каждая из которых используется для каждого процесса, а затем читать на каждой карте, используя список, чтобы отслеживать слова, которые вы уже посчитали. Предполагая, что каждое слово является ключом к подсчитанному количеству раз, вот как это выглядит. Параллельная обработка здесь может быть не лучшим выбором, учитывая параллелизм, поскольку для реального увеличения производительности все должно храниться отдельно. Если у вас есть место для хранения, то вы наверняка можете использовать список и получить в худшем случае O (N) эффективность от интеграции карт. Вам нужно будет сохранить интеграцию карт в одном потоке или одном процессе.