...

пятница, 27 сентября 2013 г.

Go: многопоточность и параллельность

Люблю Go, люблю его хвалить (бывает даже, привираю слега), люблю о нем статьи. Прочитал статью “Go: Два года в продакшне ”, потом комменты. Стало понятно, на хабре — оптимисты! Хотят верить в лучшее.



По умолчанию Go работает на одном потоке, используя свой шедулер и асинхронные вызовы. (У программиста создается ощущение многопоточности и параллельности.) В этом случае каналы работаю очень быстро. Но если указать Go использовать 2 и больше потока, то Go начинает использовать блокировки и производительность каналов может падать. Не хочется себя ограничивать в использовании каналов. Тем более, большинство сторонних библиотек при каждом удобном случае используют каналы. Поэтому часто эффективно запускать Go с одним потоком, как это сделано по умолчанию.

channel01.go



package main

import "fmt"
import "time"
import "runtime"

type Mes struct{
i int
}


func main() {

numcpu := runtime.NumCPU()
fmt.Println("NumCPU", numcpu)
//runtime.GOMAXPROCS(numcpu)
runtime.GOMAXPROCS(1)

ch1 := make(chan int)
ch2 := make(chan float64)

go func() {
for i := 0; i < 1000000; i++ {
ch1 <- i
}
ch1 <- -1
ch2 <- 0.0
}()
go func() {
total := 0.0
for {
t1 := time.Now().UnixNano()
for i := 0; i < 100000; i++ {
m := <-ch1
if m == -1 {
ch2 <- total
}
}
t2 := time.Now().UnixNano()
dt := float64(t2 - t1) / 1000000.0
total += dt
fmt.Println(dt)
}
}()

fmt.Println("Total:", <-ch2, <-ch2)
}




users-iMac:channel user$ go run channel01.go
NumCPU 4
23.901
24.189
23.957
24.072
24.001
23.807
24.039
23.854
23.798
24.1
Total: 239.718 0


теперь давайте активируем все ядра, перекомментировав строки.



runtime.GOMAXPROCS(numcpu)
//runtime.GOMAXPROCS(1)



users-iMac:channel user$ go run channel01.go
NumCPU 4
543.092
534.985
535.799
533.039
538.806
533.315
536.501
533.261
537.73
532.585
Total: 5359.113 0


20 раз медленней? В чем подвох? Размер канала по умолчанию 1.



ch1 := make(chan int)


Поставим 100.



ch1 := make(chan int, 100)


результат 1 поток



users-iMac:channel user$ go run channel01.go
NumCPU 4
9.704
9.618
9.178
9.84
9.869
9.461
9.802
9.743
9.877
9.756
Total: 0 96.848


результат 4 потока



users-iMac:channel user$ go run channel01.go
NumCPU 4
17.046
17.046
16.71
16.315
16.542
16.643
17.69
16.387
17.162
15.232
Total: 0 166.77300000000002


Всего в два раза медленней, но не всегда можно это использовать.


Пример “канал каналов”


package main

import "fmt"
import "time"
import "runtime"

type Mes struct{
ch chan int
}


func main() {

numcpu := runtime.NumCPU()
fmt.Println("NumCPU", numcpu)
//runtime.GOMAXPROCS(numcpu)
runtime.GOMAXPROCS(1)

ch1 := make(chan chan int, 100)
ch2 := make(chan float64, 1)

go func() {
t1 := time.Now().UnixNano()
for i := 0; i < 1000000; i++ {
ch := make(chan int, 100)
ch1 <- ch
<- ch
}
t2 := time.Now().UnixNano()
dt := float64(t2 - t1) / 1000000.0
fmt.Println(dt)
ch2 <- 0.0
}()
go func() {
for i := 0; i < 1000000; i++ {
ch := <-ch1
ch <- i
}
ch2 <- 0.0
}()

<-ch2
<-ch2
}


результат 1 поток



users-iMac:channel user$ go run channel03.go
NumCPU 4
1041.489




результат 4 потока

users-iMac:channel user$ go run channel03.go
NumCPU 4
11170.616




Поэтому, если у вас 8 ядер и вы пишите сервер на Go, вам не стоит полностью полагаться на Go в распараллеливании программы, а может, запустить 8 однопоточных процессов, а перед ними балансировщик, который тоже можно написать на Go. У нас в продакшине был сервер, который при переходе с одно-ядерного сервера на 4х стал обрабатывать на 10% меньше запросов.

Что значат эти цифры? Перед нами стояла задача обрабатывать 3000 запросов в секунду в одном контексте (например, выдавать каждому запросу последовательно числа: 1, 2, 3, 4, 5… может, чуть сложней) и производительность 3000 запросов в секунду ограничивается в первую очередь каналами. С добавлением потоков и ядер производительность растет не так рьяно, как хотелось. 3000 запросов в секунду для Go — это некий предел на современном оборудовании.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий