Changes

Jump to: navigation, search

DPS921/Halt and Catch Fire

2,409 bytes added, 12:24, 8 December 2016
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.
== Parallel Programming in Go ==
=== What is Go? ===
*The Go language is an open source project to make programmers more productive.<br/><br/>*It is expressive, concise, clean, and efficient. Its concurrency mechanisms make it easy to write programs that get the most out of multi-core and networked machines, while its novel type system enables flexible and modular program construction. Go compiles quickly to machine code yet has the convenience of garbage collection and the power of run-time reflection. It's a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language.  
=== By what means does Go allow parallelism? ===
*Go allows multi-core programming using concurrency methods, and enables the ability to parallelize.
*That will make our final ratio of: (π*(1)2/4) / 12 = π/4
*We just need to multiply by 4 to get π.
==== Implementation Programming implementation ========= Serial - Pi =====<syntaxhighlight lang="go">package main import ( "fmt" "math/rand" "time") //Function: Serial calculation of PIfunc 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)
return ratio = ratio * 4  watch := Stop(start) // stop timer  fmt.Printf("Serial - milliseconds elapsed:\t\t\t\t %v\n", watch.Milliseconds())  return ratio
}
</syntaxhighlight>===== Serial execution CPU profile =====As you can see only one core is work to compute the value of Pi - '''CPU8 @ 100%'''<br/>[[ImageFunction:Cpu.PNG|alt=Monte Carlo Simulations]]===== Parallel =====<syntaxhighlight lang="go">Concurrent calculation of PI
func MultiPI(samples int) float64 {
runtime.GOMAXPROCS(runtime.NumCPU())
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
}
return total = total / float64(cpus)  watch := Stop(start) // stop timer  fmt.Printf("\nConcurrent - milliseconds elapsed:\t\t\t %v\n", watch.Milliseconds())  return total} // get the number of CPUs available from the runtimefunc init() { runtime.GOMAXPROCS(runtime.NumCPU())
}
 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]]
===== Results - Efficiency profile =====
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'''.
===== Results - Serial vs parallel Performance =performance ====
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.
147
edits

Navigation menu