Операциите, свързани с процесора, използват ефективно процесора. Ако се възползваме от повече CPU, можем да направим подобрения в производителността. С други думи, ако поставим повече goroutines и се възползваме от хардуерните нишки, можем да работим паралелно и да направим нашата програма по-бърза. Ние не чакаме нищо, ние просто активно използваме хардуерни нишки паралелно. Имайте предвид, че те са обвързани с процесора операции, най-вече поставянето на повече от хардуерни нишки няма да доведе до много подобрения в производителността според моя опит. Тъй като те са обвързани с процесора операции, ако машината ми има 20 нишки, няма да има значение, ако поставя 25 goroutines, които изпълняват обвързани с процесора операции, те ще чакат в опашката и няма да правят нищо.

Бенчмаркинг

Ще използвам функцията bcrypt, която прави много математика. Така че това е обвързана с процесора операция.

Ограничение с 1 CPU

Вижте, когато извършвате обвързани с процесора операции, за разлика от I/O обвързаните операции, получаваме същия резултат, когато използваме 1 процесор.

package cpu

import "testing"
func BenchmarkSync(b *testing.B) {
 for i := 0; i < b.N; i++ {
  seq()
 }
}
func BenchmarkParallel(b *testing.B) {
 for i := 0; i < b.N; i++ {
  parallel()
 }
}

package cpu

import (
 "sync"
 "golang.org/x/crypto/bcrypt"
)
func seq() {
 for i := 0; i < 10; i++ {
  bcrypt.GenerateFromPassword([]byte("test"), 10)
 }
}
func parallel() {
 var wg sync.WaitGroup
 wg.Add(10)
 for i := 0; i < 10; i++ {
  go func() {
   defer wg.Done()
   bcrypt.GenerateFromPassword([]byte("test"), 10)
  }()
 }
 wg.Wait()
}

Дайте повече процесор

Този път ще дам 4 процесора. Обърнете внимание, че моята машина не е IDLE, така че много неща се случват на заден план, така че може би няма да мога да се възползвам от всички CPU, така че трябва да потвърдите резултатите, но можете да видите колко е по-бързо сега . Докато seq е същият, ние почти получихме 3 пъти по-бързо приложение. Бихте очаквали 4 пъти по-бързо, но хардуерните нишки не обслужват само нашето приложение go.

Стартирането на твърде много goroutines не помага при операции, свързани с процесора

Имам 4 хардуерни нишки за използване. ако стартирам 4 goroutines, ще получа почти същия резултат, когато стартирам 8 goroutines, но когато стартирам 2 goroutines, което е половината. Почти 40% губя производителност.

package cpu

import (
 "sync"
 "golang.org/x/crypto/bcrypt"
)
func seq() {
 for i := 0; i < 10; i++ {
  bcrypt.GenerateFromPassword([]byte("test"), 10)
 }
}
func parallel() {
 var wg sync.WaitGroup
 wg.Add(10)
 for i := 0; i < 10; i++ {
  go func() {
   defer wg.Done()
   bcrypt.GenerateFromPassword([]byte("test"), 10)
  }()
 }
 wg.Wait()
}
func limitParallel(n int) {
 ch := make(chan bool, n)
 var wg sync.WaitGroup
 wg.Add(n)
 for i := 0; i < n; i++ {
  go func() {
   defer wg.Done()
   for range ch {
    bcrypt.GenerateFromPassword([]byte("test"), 10)
   }
  }()
 }
 for i := 0; i < 10; i++ {
  ch <- true
 }
 close(ch)
 wg.Wait()
}
package cpu
import "testing"
func BenchmarkSync(b *testing.B) {
 for i := 0; i < b.N; i++ {
  seq()
 }
}
func BenchmarkParallel(b *testing.B) {
 for i := 0; i < b.N; i++ {
  parallel()
 }
}
func BenchmarkLimitParallel(b *testing.B) {
 for i := 0; i < b.N; i++ {
  limitParallel(4)
 }
}
func BenchmarkLimitHalfParallel(b *testing.B) {
 for i := 0; i < b.N; i++ {
  limitParallel(2)
 }
}
func BenchmarkLimitTwiceParallel(b *testing.B) {
 for i := 0; i < b.N; i++ {
  limitParallel(8)
 }
}

Заключение

Когато извършвате I/O обвързани операции, повече процесор не помага много, но когато правите обвързани с процесора операции, даването на повече процесор помага.