Last active
December 21, 2015 10:09
-
-
Save nathanwdavis/6290428 to your computer and use it in GitHub Desktop.
Messing around with really CPU intensive math ops and comparing simple for loops vs. goroutine per loop vs. partitioning into multiple goroutines (lightweight, green threads) in Go. The range partitioned goroutine-based solution tends to run around 3.5 - 4x faster than the simple for loop using 8 goroutines. The one that uses a goroutine per loo…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Example Output: | |
➜ go run partitioned_loops.go | |
Starting... | |
simpleFor result: 468767562500, time: 0.013604745000000001 sec | |
goForEach result: 468767562500, time: 24.106190332 sec | |
goPartitioned result: 468767562500, time: 0.004296383 sec | |
*/ | |
package main | |
import ( | |
"fmt" | |
"io/ioutil" | |
"runtime" | |
"time" | |
) | |
const iterations = 1e6 //must be even number for now | |
const maxThreads = 10 | |
func main() { | |
primeGoThreads(maxThreads) | |
fmt.Print("Starting...\n") | |
timeIt(simpleFor, "simpleFor") | |
timeIt(goForEach, "goForEach") //this dies and takes the machine with it if iterations is much more than 1e6 | |
//timeIt(simpleForInGo, "simpleForInGo") //no diff from simpleFor | |
timeIt(goPartitioned, "goPartitioned") | |
} | |
type action func() int64 | |
func timeIt(f action, title string) { | |
start := time.Now() | |
result := f() | |
fmt.Printf("%s \t result: %v, time: %v sec \n", | |
title, result, time.Since(start).Seconds()) | |
} | |
func inner(in int64, iteration int) int64 { | |
ret := (in + int64(iteration)*(1/2)) / 4 | |
shape := [4]int64{int64(iteration + 1), ret, in, in * 2} | |
ret = ((shape[1]*2)-(shape[0]+shape[3]))/8 + 1 | |
return ret - (int64(iteration) / 4) | |
} | |
func deriveInput(iteration int) int64 { | |
return iterations - int64(iteration) + 100 | |
} | |
func simpleFor() int64 { | |
var ret int64 | |
for i := 0; i < iterations; i++ { | |
ret -= inner(deriveInput(i), iterations) | |
} | |
return ret | |
} | |
func goForEach() int64 { | |
var ret int64 | |
ch := make(chan int64, 1000) | |
fn := func(i int, ch chan int64) { | |
ch <- inner(deriveInput(i), iterations) | |
} | |
for i := 0; i < iterations; i++ { | |
go fn(i, ch) | |
} | |
for i := 0; i < iterations; i++ { | |
ret -= <-ch | |
} | |
return ret | |
} | |
func simpleForInGo() int64 { | |
var ret int64 | |
ch := make(chan int64) | |
fn := func(ch chan int64) { | |
ch <- simpleFor() | |
} | |
go fn(ch) | |
ret = <-ch | |
return ret | |
} | |
func goPartitioned() int64 { | |
var ret int64 | |
const parts = 8 //should be a power of 2 (2,4,8,16, etc) | |
const partSize = iterations / parts | |
ch := make(chan int64) | |
fn := func(start, till int, ch chan int64) { | |
ch <- simpleForRange(start, till) | |
} | |
for i := 0; i < parts; i++ { | |
go fn(i*partSize, (i+1)*partSize, ch) | |
} | |
for i := 0; i < parts; i++ { | |
ret += <-ch | |
} | |
return ret | |
} | |
func simpleForRange(start, till int) int64 { | |
var ret int64 | |
for i := start; i < till; i++ { | |
ret -= inner(deriveInput(i), iterations) | |
} | |
return ret | |
} | |
func primeGoThreads(maxThreads int) { | |
runtime.GOMAXPROCS(maxThreads) | |
fn := func(i int) { | |
a := i / 2 | |
fmt.Fprint(ioutil.Discard, a) | |
} | |
for i := 1; i < maxThreads*100; i++ { | |
go fn(i) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment