vendredi 13 janvier 2017

Generating random numbers concurrently in Go

I'm new to Go and to concurrent/parallel programming in general. In order to try out (and hopefully see the performance benefits of) goroutines, I've put together a small test program that simply generates 100 million random ints - first in a single goroutine, and then in as many goroutines as reported by runtime.NumCPU().

However, I consistently get worse performance using more goroutines than using a single one. I assume I'm missing something vital in either my programs design or the way in which I use goroutines/channels/other Go features. Any feedback is much appreciated.

I attach the code below.

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)
    }
}




Aucun commentaire:

Enregistrer un commentaire