отредактировано, чтобы уточнить: я пытаюсь понять, как предсказать оптимальное количество Goroutines.Прочитал эту серию: https://www.ardanlabs.com/blog/2018/12/scheduling-in-go-part3.html
Мне кажется, я понял теорию, но я пытаюсь понять, как запустить оптимальное количество процедур для конкретной задачи.
Оригинальный пост:
Я сделал простую программу простого поиска (пробное разделение), чтобы играть с различным количеством Goroutines.Код работает нормально.(https://github.com/glella/primes)
Непонятно, почему производительность достигает максимума с числом логических ядер Goroutines 2X. Выводы или указатели того, почему оценили.
Я предполагал, что оптимальное количество Goroutines будет равнона количество логических ядер, но кажется, что производительность достигает максимума при использовании примерно в 2 раза больше логических ядер (независимо от того, насколько велик предел поиска - максимальный предел поиска пробовал 100 000 000 по практическим соображениям).
т.е.: в моем конце 2013 года i7 MacBookPro: реальные ядра 4, логические ядра, обнаруженные во время выполнения 8, оптимальная производительность при использовании около 16 процедур. Точно такой же вывод у меня в Note9 с Termux.
// Полный код для справки -извините за длину. Работает нормально.
// prime search - trial division
// understand optimal number of goroutines to use
package main
import (
"bufio"
"fmt"
"os"
"runtime"
"strconv"
"strings"
"time"
)
func IsPrime(n int) bool {
if n < 0 {
n = -n
}
switch {
case n == 2:
return true
case n < 2 || n%2 == 0:
return false
default:
var i int
for i = 3; i*i <= n; i += 2 {
if n%i == 0 {
return false
}
}
}
return true
}
func prompt(s string) string {
fmt.Print(s + " ")
reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')
// convert CRLF to LF
text = strings.Replace(text, "\n", "", -1)
return text
}
func getInt(s string) int {
temp, err := strconv.Atoi(s)
if err == nil {
return temp
}
return 0
}
func doSearch(s []int, c chan []int) {
var res []int
for _, val := range s {
if IsPrime(val) {
res = append(res, val)
}
}
c <- res
}
func makeRange(min, max int) []int {
a := make([]int, max-min+1)
for i := range a {
a[i] = min + i
}
return a
}
func prepSearch(n int) [][]int {
text := prompt("Number of Goroutines to use?")
numOfRout := getInt(text)
//numCPUs := runtime.NumCPU() // changed to determine number of goroutines manually
numCPUs := numOfRout
fmt.Printf("Logical CPUs: %d. Creating %d Goroutines.\n", runtime.NumCPU(), numCPUs)
rangesToSearchSlice := make([][]int, numCPUs)
rangeSize := n / numCPUs // divide the search evenly between CPUs
reminder := n % numCPUs // calculate de reminder to be spread out
// Number of ranges to search evenly = number of CPUs
range_sizes := make([]int, numCPUs)
// Even numbers per range
for i := 0; i < numCPUs; i++ {
range_sizes[i] = rangeSize
}
// Spread the reminder
for i := 0; reminder > 0; i = (i + 1) % numCPUs {
range_sizes[i] += 1
reminder -= 1
}
// Make ranges & store them
min := 1
max := n
for i := 0; i < numCPUs && max <= n; i++ {
max = min + range_sizes[i] - 1
rangeToSearch := makeRange(min, max)
rangesToSearchSlice[i] = rangeToSearch
min = max + 1
}
//fmt.Println(rangesToSearchSlice) // To test it worked properly
return rangesToSearchSlice
}
func main() {
var result []int
var num = 0
var text = ""
fmt.Println("Looks for prime numbers from 1 to your input")
for {
result = nil
num = 0
text = prompt("Seek until what integer number?")
num = getInt(text)
rangesToSearch := prepSearch(num)
l := len(rangesToSearch)
c := make(chan []int)
start := time.Now() // Start timer
for i := range rangesToSearch {
go doSearch(rangesToSearch[i], c)
}
for i := 0; i < l; i++ {
temp := <-c
result = append(result, temp...)
}
duration := time.Since(start) // End timer & duration
elapsed := float64(duration) / float64(time.Millisecond)
elapsed = elapsed / 1000.0
fmt.Printf("Found %d primes.\n", len(result))
fmt.Printf("Seconds: %.3f\n", elapsed)
text = prompt("Print primes? (y/n)")
if strings.Compare("y", text) == 0 {
fmt.Printf("%v\n", result)
}
text = prompt("Another run? (y/n)")
if strings.Compare("y", text) != 0 {
break
}
fmt.Println("---------------------")
fmt.Println("")
}
}