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 — это некий предел на современном оборудовании.

ссылка на оригинал статьи http://habrahabr.ru/post/195574/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *