Skip to content
Find file
314 lines (277 sloc) 5.98 KB
// Implementation of the Julia benchmark suite in Go.
//
// Three gonum packages must be installed, and then an additional environmental
// variable must be set to use the BLAS installation.
// To install the gonum packages, run:
// go get github.com/gonum/blas
// go get github.com/gonum/matrix/mat64
// go get github.com/gonum/stat
// The cgo ldflags must then be set to use the BLAS implementation. As an example,
// download OpenBLAS to ~/software
// git clone https://github.com/xianyi/OpenBLAS
// cd OpenBLAS
// make
// Then edit the enivorment variable to have
// export CGO_LDFLAGS="-L/$HOME/software/OpenBLAS -lopenblas"
package main
import (
"errors"
"fmt"
"log"
"math"
"math/cmplx"
"math/rand"
"strconv"
"testing"
"github.com/gonum/blas/blas64"
"github.com/gonum/blas/cgo"
"github.com/gonum/matrix/mat64"
"github.com/gonum/stat"
)
func init() {
// Use the BLAS implementation specified in CGO_LDFLAGS. This line can be
// commented out to use the native Go BLAS implementation found in
// github.com/gonum/blas/native.
blas64.Use(cgo.Implementation{})
// These are here so that toggling the BLAS implementation does not make imports unused
_ = cgo.Implementation{}
_ = blas64.General{}
}
// fibonacci
func fib(n int) int {
if n < 2 {
return n
}
return fib(n-1) + fib(n-2)
}
// quicksort
func qsort_kernel(a []float64, lo, hi int) []float64 {
i := lo
j := hi
for i < hi {
pivot := a[(lo+hi)/2]
for i <= j {
for a[i] < pivot {
i += 1
}
for a[j] > pivot {
j -= 1
}
if i <= j {
a[i], a[j] = a[j], a[i]
i += 1
j -= 1
}
}
if lo < j {
qsort_kernel(a, lo, j)
}
lo = i
j = hi
}
return a
}
var rnd = rand.New(rand.NewSource(1))
// randmatstat
func randmatstat(t int) (float64, float64) {
n := 5
v := make([]float64, t)
w := make([]float64, t)
ad := make([]float64, n*n)
bd := make([]float64, n*n)
cd := make([]float64, n*n)
dd := make([]float64, n*n)
P := mat64.NewDense(n, 4*n, nil)
Q := mat64.NewDense(2*n, 2*n, nil)
pTmp := mat64.NewDense(4*n, 4*n, nil)
qTmp := mat64.NewDense(2*n, 2*n, nil)
for i := 0; i < t; i++ {
for i := range ad {
ad[i] = rnd.NormFloat64()
bd[i] = rnd.NormFloat64()
cd[i] = rnd.NormFloat64()
dd[i] = rnd.NormFloat64()
}
a := mat64.NewDense(n, n, ad)
b := mat64.NewDense(n, n, bd)
c := mat64.NewDense(n, n, cd)
d := mat64.NewDense(n, n, dd)
P.Copy(a)
P.View(0, n, n, n).(*mat64.Dense).Copy(b)
P.View(0, 2*n, n, n).(*mat64.Dense).Copy(c)
P.View(0, 3*n, n, n).(*mat64.Dense).Copy(d)
Q.Copy(a)
Q.View(0, n, n, n).(*mat64.Dense).Copy(b)
Q.View(n, 0, n, n).(*mat64.Dense).Copy(c)
Q.View(n, n, n, n).(*mat64.Dense).Copy(d)
pTmp.Mul(P.T(), P)
pTmp.Pow(pTmp, 4)
qTmp.Mul(Q.T(), Q)
qTmp.Pow(qTmp, 4)
v[i] = mat64.Trace(pTmp)
w[i] = mat64.Trace(qTmp)
}
mv, stdv := stat.MeanStdDev(v, nil)
mw, stdw := stat.MeanStdDev(v, nil)
return stdv / mv, stdw / mw
}
// randmatmul
func randmatmul(n int) *mat64.Dense {
aData := make([]float64, n*n)
for i := range aData {
aData[i] = rnd.Float64()
}
a := mat64.NewDense(n, n, aData)
bData := make([]float64, n*n)
for i := range bData {
bData[i] = rnd.Float64()
}
b := mat64.NewDense(n, n, bData)
var c mat64.Dense
c.Mul(a, b)
return &c
}
// mandelbrot
func mandel(z complex128) int {
maxiter := 80
c := z
for n := 0; n < maxiter; n++ {
if cmplx.Abs(z) > 2 {
return n
}
z = z*z + c
}
return maxiter
}
// mandelperf
func mandelperf() int {
mandel_sum := 0
// These loops are constructed as such because mandel is very sensitive to
// its input and this avoids very small floating point issues.
for re := -20.0; re <= 5; re += 1 {
for im := -10.0; im <= 10; im += 1 {
m := mandel(complex(re/10, im/10))
mandel_sum += m
}
}
return mandel_sum
}
// pisum
func pisum() float64 {
var sum float64
for i := 0; i < 500; i++ {
sum = 0.0
for k := 1.0; k <= 10000; k += 1 {
sum += 1.0 / (k * k)
}
}
return sum
}
func print_perf(name string, time float64) {
fmt.Printf("go,%v,%v\n", name, time*1000)
}
// run tests
func assert(b *testing.B, t bool) {
if t != true {
b.Fatal("assert failed")
}
}
func main() {
for _, bm := range benchmarks {
seconds, err := runBenchmarkFor(bm.fn)
if err != nil {
log.Fatalf("%s %s", bm.name, err)
}
print_perf(bm.name, seconds)
}
}
func runBenchmarkFor(fn func(*testing.B)) (seconds float64, err error) {
bm := testing.Benchmark(fn)
if (bm == testing.BenchmarkResult{}) {
return 0, errors.New("failed")
}
return bm.T.Seconds() / float64(bm.N), nil
}
var benchmarks = []struct {
name string
fn func(*testing.B)
}{
{
name: "fib",
fn: func(b *testing.B) {
for i := 0; i < b.N; i++ {
if fib(20) != 6765 {
b.Fatal("unexpected value for fib(20)")
}
}
},
},
{
name: "parse_int",
fn: func(b *testing.B) {
for i := 0; i < b.N; i++ {
for k := 0; k < 1000; k++ {
n := rnd.Uint32()
m, _ := strconv.ParseUint(strconv.FormatUint(uint64(n), 16), 16, 32)
if uint32(m) != n {
b.Fatal("incorrect value for m")
}
}
}
},
},
{
name: "mandel",
fn: func(b *testing.B) {
for i := 0; i < b.N; i++ {
if mandelperf() != 14791 {
b.Fatal("unexpected value for mandelperf")
}
}
},
},
{
name: "quicksort",
fn: func(b *testing.B) {
lst := make([]float64, 5000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for k := range lst {
lst[k] = rnd.Float64()
}
qsort_kernel(lst, 0, len(lst)-1)
}
},
},
{
name: "pi_sum",
fn: func(b *testing.B) {
for i := 0; i < b.N; i++ {
if math.Abs(pisum()-1.644834071848065) >= 1e-6 {
b.Fatal("pi_sum out of range")
}
}
},
},
{
name: "rand_mat_stat",
fn: func(b *testing.B) {
for i := 0; i < b.N; i++ {
c1, c2 := randmatstat(1000)
assert(b, 0.5 < c1)
assert(b, c1 < 1.0)
assert(b, 0.5 < c2)
assert(b, c2 < 1.0)
}
},
},
{
name: "rand_mat_mul",
fn: func(b *testing.B) {
for i := 0; i < b.N; i++ {
c := randmatmul(1000)
assert(b, c.At(0, 0) >= 0)
}
},
},
}
Jump to Line
Something went wrong with that request. Please try again.