147
edits
Changes
→Serial - Pi
= Halt and Catch Fire =
Project: '''Parallelism with Go'''<br/>Presentation: [https://goo.gl/qOIQ3A Slides]
== Group Members ==
#Colin Paul [mailto:cpaul12@myseneca.ca?subject=DPS921%20from%20CDOT%20Wiki] Research etc.
*We just need to multiply by 4 to get π.
==== Programming implementation ====
===== Serial - Pi =====
<syntaxhighlight lang="go">
package main
import (
"fmt"
"math/rand"
"time"
)
//Function: Serial calculation of PI
func PI(samples int) float64 {
var inside int = 0
for i := 0; i < samples; i++ {
x := rand.Float64()
y := rand.Float64()
if (x*x + y*y) < 1 {
inside++
}
}
ratio := float64(inside) / float64(samples)
return ratio * 4
}
func init() {
rand.Seed(time.Now().UnixNano())
}
func main() {
fmt.Println("Our value of Pi after 100 runs:\t\t\t", PI(100))
fmt.Println("Our value of Pi after 1,000 runs:\t\t", PI(1000))
fmt.Println("Our value of Pi after 10,000 runs:\t\t", PI(10000))
fmt.Println("Our value of Pi after 100,000 runs:\t\t", PI(100000))
fmt.Println("Our value of Pi after 1,000,000 runs:\t\t", PI(1000000))
fmt.Println("Our value of Pi after 10,000,000 runs:\t\t", PI(10000000))
fmt.Println("Our value of Pi after 100,000,000 runs:\t\t", PI(100000000))
}</syntaxhighlight>
===== Serial execution CPU profile =====
As you can see only one core is working to compute the value of Pi - '''CPU8 @ 100%'''<br/>
[[Image:Cpu.PNG|alt=Monte Carlo Simulations]]
===== Parallel - Pi =====
<syntaxhighlight lang="go">
package main
import (
"fmt"
"math/rand"
"runtime"
"time"
)
// Struct: Stopwatch object
type StopWatch struct {
start, stop time.Time
}
// Method: Calculate time delta
func (self *StopWatch) Milliseconds() uint32 {
return uint32(self.stop.Sub(self.start) / time.Millisecond)
}
// Function: Start timer
func Start() time.Time {
return time.Now()
}
// Function: Stop timer
func Stop(start time.Time) *StopWatch {
watch := StopWatch{start: start, stop: time.Now()}
return &watch
}
// Function: Serial calculation of PI
func PI(samples int) float64 {
var inside int = 0
r := rand.New(rand.NewSource(time.Now().UnixNano()))
start := Start() // start timer
for i := 0; i < samples; i++ {
ratio := float64(inside) / float64(samples)
}
func MultiPI(samples int) float64 {
cpus := runtime.NumCPU()// getting the numbre of CPUs
fmt.Println("\nNumber of CPUs:\t\t\t\t\t\t", cpus) threadSamples := samples / cpus// splitting work among the CPUs
results := make(chan float64, cpus)
start := Start() // start timer
for j := 0; j < cpus; j++ {
go func() {// spawn goroutine
var inside int
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < threadSamples; i++ {
x, y := r.Float64(), r.Float64()
}
}
results <- float64(inside) / float64(threadSamples) * 4
}()
var total float64
// accumulate the results of reach channel
for i := 0; i < cpus; i++ {
total += <-results
}
}
func main() { fmt.Println("Running Monte Carlo simulations ...\n") fmt.Println("Our value of Pi after 1,000,000,000 runs:\t\t", PI(1000000000)) fmt.Println("Our value of Pi after 1,000,000,000 runs (MT):\t\t", MultiPI(1000000000))}</syntaxhighlight>
===== Parallel execution CPU profile =====
As you can see all eight cores are working to compute the value of Pi - '''CPU1-8 @ 100%'''<br/>
[[Image:Cpu2.PNG|alt=Monte Carlo Simulations]]
Profiling the execution time at a computation hot spot for both the serial and parallel versions of the Monte Carlo Simulations program.<br/>
[[Image:EP.PNG|alt=Monte Carlo Simulations]]<br/>
We can see that the parallel version gives us about a '''~4x performance gain'''.
Evaluating the time it takes the serial and parallel versions of the program to run through the Monte Carlo Iterations for 1,000,000,000 (1 billion) iterations.<br/>
[[Image:MCSim.PNG|alt=Monte Carlo Simulations]]<br/>
We can see that the parallel version gives us about a '''~4x performance gain''' matching our benchmark results.