编辑以澄清:我正在尝试了解如何预测Goroutine的最佳数量。已阅读本系列:https://www.ardanlabs.com/blog/2018/12/scheduling-in-go-part3.html
我想我已经掌握了理论,但是我试图了解如何针对特定任务启动最佳数量的例程。
原始帖子:
我制作了一个简单的素数搜索(试算)程序来处理不同数量的Goroutine。代码正常。 (https://github.com/glella/primes)
不清楚为什么Goroutines的逻辑内核数量是2倍时性能达到峰值。关于为什么被赞赏的见解或指针。
我曾经假设Goroutine的最佳数量将等于逻辑核心的数量,但是当使用大约2倍的逻辑核心数量时,性能似乎达到了顶峰。 (无论搜索限制有多大,出于实际原因,最高搜索限制都尝试了100,000,000。)
即:在我的2013年末i7 MacBookPro上:实际核心4,在运行时检测到逻辑核心8,使用约16个goroutine时的最佳性能。与Termux在我的Note9上得出的结论完全相同。
//供参考的完整代码-抱歉,长度不足。可以。
// 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("")
}
}