Skip to content

Commit 6e88755

Browse files
committed
test/bench/go1: first draft of Go 1 benchmark suite
I have included a few important microbenchmarks, but the overall intent is to have mostly end-to-end benchmarks timing real world operations. The jsondata.go file is a summary of agl's activity in various open source repositories. It gets used as test data for many of the benchmarks. Everything links into one binary (even the test data) so that it is easy to run the benchmarks on many computers: there is just one file to copy around. R=golang-dev, r, bradfitz, adg, r CC=golang-dev https://golang.org/cl/5484071
1 parent d10126a commit 6e88755

File tree

13 files changed

+2553
-1
lines changed

13 files changed

+2553
-1
lines changed

src/clean.bash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ for i in lib9 libbio libmach cmd pkg \
2424
../misc/cgo/life ../misc/cgo/test \
2525
../misc/dashboard/builder ../misc/goplay\
2626
../doc/codelab/wiki\
27-
../test/bench/shootout ../test/bench/garbage
27+
../test/bench/shootout ../test/bench/garbage ../test/bench/go1
2828
do
2929
# Do not use gomake here. It may not be available.
3030
$MAKE -C "$GOROOT/src/$i" clean

src/run.bash

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ done
105105
./timing.sh -test
106106
) || exit $?
107107

108+
(xcd ../test/bench/go1
109+
gomake test
110+
) || exit $?
111+
108112
(xcd ../test
109113
./run
110114
) || exit $?

test/bench/go1/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
include $(GOROOT)/src/Make.inc
2+
3+
TARG=go1
4+
GOFILES=\
5+
dummy.go\
6+
7+
include $(GOROOT)/src/Make.pkg

test/bench/go1/binarytree_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2011 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// This benchmark, taken from the shootout, tests garbage collector
6+
// performance by generating and discarding large binary trees.
7+
8+
package go1
9+
10+
import "testing"
11+
12+
type binaryNode struct {
13+
item int
14+
left, right *binaryNode
15+
}
16+
17+
func bottomUpTree(item, depth int) *binaryNode {
18+
if depth <= 0 {
19+
return &binaryNode{item: item}
20+
}
21+
return &binaryNode{item, bottomUpTree(2*item-1, depth-1), bottomUpTree(2*item, depth-1)}
22+
}
23+
24+
func (n *binaryNode) itemCheck() int {
25+
if n.left == nil {
26+
return n.item
27+
}
28+
return n.item + n.left.itemCheck() - n.right.itemCheck()
29+
}
30+
31+
const minDepth = 4
32+
33+
func binarytree(n int) {
34+
maxDepth := n
35+
if minDepth+2 > n {
36+
maxDepth = minDepth + 2
37+
}
38+
stretchDepth := maxDepth + 1
39+
40+
check := bottomUpTree(0, stretchDepth).itemCheck()
41+
//fmt.Printf("stretch tree of depth %d\t check: %d\n", stretchDepth, check)
42+
43+
longLivedTree := bottomUpTree(0, maxDepth)
44+
45+
for depth := minDepth; depth <= maxDepth; depth += 2 {
46+
iterations := 1 << uint(maxDepth-depth+minDepth)
47+
check = 0
48+
49+
for i := 1; i <= iterations; i++ {
50+
check += bottomUpTree(i, depth).itemCheck()
51+
check += bottomUpTree(-i, depth).itemCheck()
52+
}
53+
//fmt.Printf("%d\t trees of depth %d\t check: %d\n", iterations*2, depth, check)
54+
}
55+
longLivedTree.itemCheck()
56+
//fmt.Printf("long lived tree of depth %d\t check: %d\n", maxDepth, longLivedTree.itemCheck())
57+
}
58+
59+
func BenchmarkBinaryTree17(b *testing.B) {
60+
for i := 0; i < b.N; i++ {
61+
binarytree(17)
62+
}
63+
}

test/bench/go1/dummy.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package go1
2+
3+
// Nothing to see here: everything is in the _test files.

test/bench/go1/fannkuch_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2011 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// This benchmark, taken from the shootout, tests array indexing
6+
// and array bounds elimination performance.
7+
8+
package go1
9+
10+
import "testing"
11+
12+
func fannkuch(n int) int {
13+
if n < 1 {
14+
return 0
15+
}
16+
17+
n1 := n - 1
18+
perm := make([]int, n)
19+
perm1 := make([]int, n)
20+
count := make([]int, n)
21+
22+
for i := 0; i < n; i++ {
23+
perm1[i] = i // initial (trivial) permutation
24+
}
25+
26+
r := n
27+
didpr := 0
28+
flipsMax := 0
29+
for {
30+
if didpr < 30 {
31+
didpr++
32+
}
33+
for ; r != 1; r-- {
34+
count[r-1] = r
35+
}
36+
37+
if perm1[0] != 0 && perm1[n1] != n1 {
38+
flips := 0
39+
for i := 1; i < n; i++ { // perm = perm1
40+
perm[i] = perm1[i]
41+
}
42+
k := perm1[0] // cache perm[0] in k
43+
for { // k!=0 ==> k>0
44+
for i, j := 1, k-1; i < j; i, j = i+1, j-1 {
45+
perm[i], perm[j] = perm[j], perm[i]
46+
}
47+
flips++
48+
// Now exchange k (caching perm[0]) and perm[k]... with care!
49+
j := perm[k]
50+
perm[k] = k
51+
k = j
52+
if k == 0 {
53+
break
54+
}
55+
}
56+
if flipsMax < flips {
57+
flipsMax = flips
58+
}
59+
}
60+
61+
for ; r < n; r++ {
62+
// rotate down perm[0..r] by one
63+
perm0 := perm1[0]
64+
for i := 0; i < r; i++ {
65+
perm1[i] = perm1[i+1]
66+
}
67+
perm1[r] = perm0
68+
count[r]--
69+
if count[r] > 0 {
70+
break
71+
}
72+
}
73+
if r == n {
74+
return flipsMax
75+
}
76+
}
77+
return 0
78+
}
79+
80+
func BenchmarkFannkuch11(b *testing.B) {
81+
for i := 0; i < b.N; i++ {
82+
fannkuch(11)
83+
}
84+
}

test/bench/go1/fasta_test.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright 2011 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package go1
6+
7+
// Not a benchmark; input for revcomp.
8+
9+
var fasta25m = fasta(25e6)
10+
11+
func fasta(n int) []byte {
12+
out := make(fastaBuffer, 0, 11*n)
13+
14+
iub := []fastaAcid{
15+
{prob: 0.27, sym: 'a'},
16+
{prob: 0.12, sym: 'c'},
17+
{prob: 0.12, sym: 'g'},
18+
{prob: 0.27, sym: 't'},
19+
{prob: 0.02, sym: 'B'},
20+
{prob: 0.02, sym: 'D'},
21+
{prob: 0.02, sym: 'H'},
22+
{prob: 0.02, sym: 'K'},
23+
{prob: 0.02, sym: 'M'},
24+
{prob: 0.02, sym: 'N'},
25+
{prob: 0.02, sym: 'R'},
26+
{prob: 0.02, sym: 'S'},
27+
{prob: 0.02, sym: 'V'},
28+
{prob: 0.02, sym: 'W'},
29+
{prob: 0.02, sym: 'Y'},
30+
}
31+
32+
homosapiens := []fastaAcid{
33+
{prob: 0.3029549426680, sym: 'a'},
34+
{prob: 0.1979883004921, sym: 'c'},
35+
{prob: 0.1975473066391, sym: 'g'},
36+
{prob: 0.3015094502008, sym: 't'},
37+
}
38+
39+
alu := []byte(
40+
"GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" +
41+
"GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" +
42+
"CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" +
43+
"ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" +
44+
"GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" +
45+
"AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" +
46+
"AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA")
47+
48+
out.WriteString(">ONE Homo sapiens alu\n")
49+
fastaRepeat(&out, alu, 2*n)
50+
out.WriteString(">TWO IUB ambiguity codes\n")
51+
fastaRandom(&out, iub, 3*n)
52+
out.WriteString(">THREE Homo sapiens frequency\n")
53+
fastaRandom(&out, homosapiens, 5*n)
54+
return out
55+
}
56+
57+
type fastaBuffer []byte
58+
59+
func (b *fastaBuffer) Flush() {
60+
panic("flush")
61+
}
62+
63+
func (b *fastaBuffer) WriteString(s string) {
64+
p := b.NextWrite(len(s))
65+
copy(p, s)
66+
}
67+
68+
func (b *fastaBuffer) NextWrite(n int) []byte {
69+
p := *b
70+
if len(p)+n > cap(p) {
71+
b.Flush()
72+
p = *b
73+
}
74+
out := p[len(p) : len(p)+n]
75+
*b = p[:len(p)+n]
76+
return out
77+
}
78+
79+
const fastaLine = 60
80+
81+
func fastaRepeat(out *fastaBuffer, alu []byte, n int) {
82+
buf := append(alu, alu...)
83+
off := 0
84+
for n > 0 {
85+
m := n
86+
if m > fastaLine {
87+
m = fastaLine
88+
}
89+
buf1 := out.NextWrite(m + 1)
90+
copy(buf1, buf[off:])
91+
buf1[m] = '\n'
92+
if off += m; off >= len(alu) {
93+
off -= len(alu)
94+
}
95+
n -= m
96+
}
97+
}
98+
99+
const (
100+
fastaLookupSize = 4096
101+
fastaLookupScale float64 = fastaLookupSize - 1
102+
)
103+
104+
var fastaRand uint32 = 42
105+
106+
type fastaAcid struct {
107+
sym byte
108+
prob float64
109+
cprob float64
110+
next *fastaAcid
111+
}
112+
113+
func fastaComputeLookup(acid []fastaAcid) *[fastaLookupSize]*fastaAcid {
114+
var lookup [fastaLookupSize]*fastaAcid
115+
var p float64
116+
for i := range acid {
117+
p += acid[i].prob
118+
acid[i].cprob = p * fastaLookupScale
119+
if i > 0 {
120+
acid[i-1].next = &acid[i]
121+
}
122+
}
123+
acid[len(acid)-1].cprob = 1.0 * fastaLookupScale
124+
125+
j := 0
126+
for i := range lookup {
127+
for acid[j].cprob < float64(i) {
128+
j++
129+
}
130+
lookup[i] = &acid[j]
131+
}
132+
133+
return &lookup
134+
}
135+
136+
func fastaRandom(out *fastaBuffer, acid []fastaAcid, n int) {
137+
const (
138+
IM = 139968
139+
IA = 3877
140+
IC = 29573
141+
)
142+
lookup := fastaComputeLookup(acid)
143+
for n > 0 {
144+
m := n
145+
if m > fastaLine {
146+
m = fastaLine
147+
}
148+
buf := out.NextWrite(m + 1)
149+
f := fastaLookupScale / IM
150+
myrand := fastaRand
151+
for i := 0; i < m; i++ {
152+
myrand = (myrand*IA + IC) % IM
153+
r := float64(int(myrand)) * f
154+
a := lookup[int(r)]
155+
for a.cprob < r {
156+
a = a.next
157+
}
158+
buf[i] = a.sym
159+
}
160+
fastaRand = myrand
161+
buf[m] = '\n'
162+
n -= m
163+
}
164+
}

0 commit comments

Comments
 (0)