Как мне перевести следующую модель потоков из C ++, чтобы перейти? - PullRequest
2 голосов
/ 22 октября 2011

В моем проекте C ++ у меня есть большой двоичный файл ГБ на диске, который я считываю в память для вычислений только для чтения.

Моя текущая реализация на C ++ включает в себя считывание всего фрагмента в память один раз, а затем порождает потоки для чтения из фрагмента для выполнения различных вычислений (без мьютекса и выполняется быстро). Технически, каждый поток действительно нуждается только в небольшой части файла за раз, поэтому в будущем я могу изменить эту реализацию на использование mmap (), особенно если файл становится слишком большим. Я заметил это gommap lib , так что я думаю, что я должен быть покрыт в будущем.

Какой подход я должен использовать, чтобы перевести мою текущую модель потоков C ++ (один большой кусок памяти только для чтения) в модель потоков Go, помня об эффективности времени выполнения?

goroutines? альтернативы?

Ответы [ 2 ]

3 голосов
/ 22 октября 2011

Я уверен, что этот ответ выдержит много тепла, но здесь идет:

Вы не уменьшите время выполнения, переключившись на Go, особенно если ваш код уже не содержит мьютексов. Go не гарантирует эффективную балансировку программ и в настоящее время не будет наилучшим образом использовать имеющиеся ядра. Сгенерированный код медленнее, чем C ++. Сильные стороны Го в чистых абстракциях и параллелизме, не параллелизм.

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

Если есть какая-либо интенсивная межпрограммная связь или зависимости между данными, вы должны попытаться сделать алгоритм однопоточным. Трудно сказать, не зная больше о подпрограммах, которые вы применяете к данным, но вполне возможно, что вы преждевременно извлекли потоки в надежде на волшебное повышение производительности.

Если вы не можете полагаться на отображение памяти из-за размера файла или других ограничений платформы, вам следует рассмотреть возможность использования вызова pread, тем самым повторно используя один дескриптор файла и читая только по мере необходимости.

Как всегда, следующее правило относится к оптимизации. Вы должны профиль. Вы должны проверить, что изменения, которые вы вносите в рабочее решение, улучшают ситуацию. Очень часто вы обнаружите, что отображение памяти, многопоточность и другие махинации никак не влияют на производительность. Это также тяжелая битва, если вы переходите с C или C ++.

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

1 голос
/ 22 октября 2011

Эта программа суммирует все байты в файле в нескольких процедурах (не беспокоясь о переполнении).

Вы захотите переопределить processChunk и aggregateResults для вашего случая. Вы также можете изменить тип канала канала результатов. В зависимости от того, что вы делаете, вам может даже не потребоваться агрегировать результаты. Размер порции и размер буфера канала - это другие регуляторы, которые вы можете настроить.

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    data, err := ioutil.ReadFile("filename")
    if err != nil {
        // handle this error somehow
        panic(err.String())
    }
    // Adjust this to control the size of chunks.
    // I've chosen it arbitrarily.
    const chunkSize = 0x10000
    // This channel's unbuffered. Add a buffer for better performance.
    results := make(chan int64)

    chunks := 0    
    for len(data) > 0 {
        size := chunkSize
        if len(data) < chunkSize {
            size = len(data)
        }
        go processChunk(data[:size], results)
        data = data[size:]
        chunks++
    }

    aggregateResults(results, chunks)
}

func processChunk(chunk []byte, results chan int64) {
    sum := int64(0)
    for _, b := range chunk {
        sum += int64(b)
    }
    results <- sum
}

func aggregateResults(results chan int64, chunks int) {
    sum := int64(0)
    for chunks > 0 {
        sum += <-results
        chunks--
    }
    fmt.Println("The sum of all bytes is", sum)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...