Ich bin neu in Go und parallel/parallele Programmierung im Allgemeinen. Um die goroutines auszuprobieren (und hoffentlich die Leistungsvorteile zu sehen), habe ich ein kleines Testprogramm zusammengestellt, das einfach 100 Millionen zufällige int
s erzeugt - zuerst in einer einzigen goroutine und dann in so vielen goroutines wie von runtime.NumCPU()
gemeldet .Generieren von Zufallszahlen gleichzeitig in Go
Allerdings bekomme ich immer schlechtere Leistung mit mehr Göroutinen als mit einem einzigen. Ich vermute, dass mir etwas Wichtiges fehlt, entweder in meinem Programmdesign oder in der Art und Weise, wie ich Goroutinen/Kanäle/andere Go-Funktionen nutze. Jede Rückmeldung wird sehr geschätzt.
Ich füge den Code unten an.
package main
import "fmt"
import "time"
import "math/rand"
import "runtime"
func main() {
// Figure out how many CPUs are available and tell Go to use all of them
numThreads := runtime.NumCPU()
runtime.GOMAXPROCS(numThreads)
// Number of random ints to generate
var numIntsToGenerate = 100000000
// Number of ints to be generated by each spawned goroutine thread
var numIntsPerThread = numIntsToGenerate/numThreads
// Channel for communicating from goroutines back to main function
ch := make(chan int, numIntsToGenerate)
// Slices to keep resulting ints
singleThreadIntSlice := make([]int, numIntsToGenerate, numIntsToGenerate)
multiThreadIntSlice := make([]int, numIntsToGenerate, numIntsToGenerate)
fmt.Printf("Initiating single-threaded random number generation.\n")
startSingleRun := time.Now()
// Generate all of the ints from a single goroutine, retrieve the expected
// number of ints from the channel and put in target slice
go makeRandomNumbers(numIntsToGenerate, ch)
for i := 0; i < numIntsToGenerate; i++ {
singleThreadIntSlice = append(singleThreadIntSlice,(<-ch))
}
elapsedSingleRun := time.Since(startSingleRun)
fmt.Printf("Single-threaded run took %s\n", elapsedSingleRun)
fmt.Printf("Initiating multi-threaded random number generation.\n")
startMultiRun := time.Now()
// Run the designated number of goroutines, each of which generates its
// expected share of the total random ints, retrieve the expected number
// of ints from the channel and put in target slice
for i := 0; i < numThreads; i++ {
go makeRandomNumbers(numIntsPerThread, ch)
}
for i := 0; i < numIntsToGenerate; i++ {
multiThreadIntSlice = append(multiThreadIntSlice,(<-ch))
}
elapsedMultiRun := time.Since(startMultiRun)
fmt.Printf("Multi-threaded run took %s\n", elapsedMultiRun)
}
func makeRandomNumbers(numInts int, ch chan int) {
source := rand.NewSource(time.Now().UnixNano())
generator := rand.New(source)
for i := 0; i < numInts; i++ {
ch <- generator.Intn(numInts*100)
}
}
Okay, das macht alles sehr sinnvoll. Ich vermutete, dass dies mit den Kosten verbunden sein könnte, die mit der Nutzung des Kanals verbunden sind (obwohl ich nicht in Bezug auf den Kommunikationsaufwand in Bezug auf die Arbeitseinheit denke, sondern eher in Bezug auf mögliche Überlastung des Kanals) oder etwas ähnliches). Vielen Dank für die klare und pädagogische Lösung! – Karl