Одно из применений идей, описанных в предыдущих постах о go-процедурах и каналах - это распараллеливание вычисления по нескольким ядрам процессора. Если вычисление можно разбить на отдельные части, которые могут выполняться независимо, тогда вычисление может быть распараллелено, с использованием каналов, чтобы сигнализировать, когда каждая часть вычисления завершается.
Допустим, у нас есть дорогая операция для вектора элементов, и что значение операции для каждого элемента является независимым, как в следующем идеализированном примере.
type Vector []float64
// Применяеи операцию к v[i], v[i+1] ... до v[n-1].
func (v Vector) DoSome(i, n int, u Vector, c chan int) {
for ; i < n; i++ {
v[i] += u.Op(v[i])
}
c <- 1 // сигнализируем что эта часть выполнена
}
Мы запускаем части вычисления независимо друг от друга, по одному на каждом ядре процессора. Они могут быть выполнены в любом порядке, но это не имеет значения; мы только считаем сигналы завершения, опустошив канал после запуска всех go-процедур.
const numCPU = 4 // количество ядер процессора
func (v Vector) DoAll(u Vector) {
// Буферизация необязательна, но разумна
c := make(chan int, numCPU)
for i := 0; i < numCPU; i++ {
go v.DoSome(i*len(v)/numCPU,
(i+1)*len(v)/numCPU, u, c)
}
// Опустошаем канал
for i := 0; i < numCPU; i++ {
<-c // ждем завершения одного задания
}
// Все выполнено
}
Вместо того, чтобы создавать постоянное значение для numCPU, мы можем спросить среду выполнения, какое значение уместно. Функция runtime.NumCPU
возвращает количество аппаратных ядер процессора на машине, таким образом мы можем написать:
var numCPU = runtime.NumCPU()
Также есть функция runtime.GOMAXPROCS
, которая сообщает (или устанавливает) указанное пользователем количество ядер, которое может запустить Go программа одновременно. По умолчанию используется значение runtime.NumCPU
, но оно может быть переопределено установкой одноименной переменной среды оболочки или вызывом этой функции с положительным номером. Вызов этой функции с нолем просто запрашивает значение. Поэтому, если мы хотим удовлетворить запрос ресурса пользователя, мы должны написать:
var numCPU = runtime.GOMAXPROCS(0)
Убедитесь, что не путаете идеи параллелизма - структурирования программы как независимо выполняемых компонентов - и параллелизма - выполнения параллельных вычислений для повышения эффективности на нескольких ядрах процессора. Хотя функции параллелизма Go могут облегчить некоторые проблемы структурирования как параллельных вычислений, Go - это многопоточный язык, не параллельный, и не все проблемы распараллеливания соответствуют модели Go.
Читайте также:
- Эффективный Go: каналы
- Эффективный Go: параллелизм, go-процедуры (goroutines)
- Основы Go: буферизированные каналы
Комментариев нет:
Отправить комментарий