mirror of
https://github.com/golang/go
synced 2024-11-24 20:30:14 -07:00
6a44a3aa9f
The go1 benchmark suite does a lot of work at package init time, which makes it take quite a while to run even if you're not running any of the benchmarks, or if you're only running a subset of them. This leads to an awkward workaround in dist test to compile but not run the package, unlike roughly all other packages. It also reduces isolation between benchmarks by affecting the starting heap size of all benchmarks. Fix this by initializing all data required by a benchmark when that benchmark runs, and keeping it local so it gets freed by the GC and doesn't leak between benchmarks. Now, none of the benchmarks depend on global state. Re-initializing the data on each benchmark run does add overhead to an actual benchmark run, as each benchmark function is called several times with different values of b.N. A full run of all benchmarks at the default -benchtime=1s now takes ~10% longer; higher -benchtimes would be less. It would be quite difficult to cache this data between invocations of the same benchmark function without leaking between different benchmarks and affecting GC overheads, as the testing package doesn't provide any mechanism for this. This reduces the time to run the binary with no benchmarks from 1.5 seconds to 10 ms, and also reduces the memory required to do this from 342 MiB to 17 MiB. To make sure data was not leaking between different benchmarks, I ran the benchmarks with -shuffle=on. The variance remained low: mostly under 3%. A few benchmarks had higher variance, but in all cases it was similar to the variance between this change. This CL naturally changes the measured performance of several of the benchmarks because it dramatically changes the heap size and hence GC overheads. However, going forward the benchmarks should be much better isolated. For #37486. Change-Id: I252ebea703a9560706cc1990dc5ad22d1927c7a0 Reviewed-on: https://go-review.googlesource.com/c/go/+/443336 TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Michael Pratt <mpratt@google.com> Run-TryBot: Austin Clements <austin@google.com>
178 lines
3.7 KiB
Go
178 lines
3.7 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package go1
|
|
|
|
import "runtime"
|
|
|
|
// Not a benchmark; input for revcomp.
|
|
|
|
func makefasta() []byte {
|
|
var n int = 25e6
|
|
if runtime.GOARCH == "arm" || runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" {
|
|
// TODO(dfc) remove this limitation after precise gc.
|
|
// A value of 25e6 consumes 465mb of heap on 32bit
|
|
// platforms, which is too much for some systems.
|
|
// A value of 25e5 produces a memory layout that
|
|
// confuses the gc on 32bit platforms. So 25e4 it is.
|
|
n = 25e4
|
|
}
|
|
return fasta(n)
|
|
}
|
|
|
|
func fasta(n int) []byte {
|
|
out := make(fastaBuffer, 0, 11*n)
|
|
|
|
iub := []fastaAcid{
|
|
{prob: 0.27, sym: 'a'},
|
|
{prob: 0.12, sym: 'c'},
|
|
{prob: 0.12, sym: 'g'},
|
|
{prob: 0.27, sym: 't'},
|
|
{prob: 0.02, sym: 'B'},
|
|
{prob: 0.02, sym: 'D'},
|
|
{prob: 0.02, sym: 'H'},
|
|
{prob: 0.02, sym: 'K'},
|
|
{prob: 0.02, sym: 'M'},
|
|
{prob: 0.02, sym: 'N'},
|
|
{prob: 0.02, sym: 'R'},
|
|
{prob: 0.02, sym: 'S'},
|
|
{prob: 0.02, sym: 'V'},
|
|
{prob: 0.02, sym: 'W'},
|
|
{prob: 0.02, sym: 'Y'},
|
|
}
|
|
|
|
homosapiens := []fastaAcid{
|
|
{prob: 0.3029549426680, sym: 'a'},
|
|
{prob: 0.1979883004921, sym: 'c'},
|
|
{prob: 0.1975473066391, sym: 'g'},
|
|
{prob: 0.3015094502008, sym: 't'},
|
|
}
|
|
|
|
alu := []byte(
|
|
"GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" +
|
|
"GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" +
|
|
"CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" +
|
|
"ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" +
|
|
"GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" +
|
|
"AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" +
|
|
"AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA")
|
|
|
|
out.WriteString(">ONE Homo sapiens alu\n")
|
|
fastaRepeat(&out, alu, 2*n)
|
|
out.WriteString(">TWO IUB ambiguity codes\n")
|
|
fastaRandom(&out, iub, 3*n)
|
|
out.WriteString(">THREE Homo sapiens frequency\n")
|
|
fastaRandom(&out, homosapiens, 5*n)
|
|
return out
|
|
}
|
|
|
|
type fastaBuffer []byte
|
|
|
|
func (b *fastaBuffer) Flush() {
|
|
panic("flush")
|
|
}
|
|
|
|
func (b *fastaBuffer) WriteString(s string) {
|
|
p := b.NextWrite(len(s))
|
|
copy(p, s)
|
|
}
|
|
|
|
func (b *fastaBuffer) NextWrite(n int) []byte {
|
|
p := *b
|
|
if len(p)+n > cap(p) {
|
|
b.Flush()
|
|
p = *b
|
|
}
|
|
out := p[len(p) : len(p)+n]
|
|
*b = p[:len(p)+n]
|
|
return out
|
|
}
|
|
|
|
const fastaLine = 60
|
|
|
|
func fastaRepeat(out *fastaBuffer, alu []byte, n int) {
|
|
buf := append(alu, alu...)
|
|
off := 0
|
|
for n > 0 {
|
|
m := n
|
|
if m > fastaLine {
|
|
m = fastaLine
|
|
}
|
|
buf1 := out.NextWrite(m + 1)
|
|
copy(buf1, buf[off:])
|
|
buf1[m] = '\n'
|
|
if off += m; off >= len(alu) {
|
|
off -= len(alu)
|
|
}
|
|
n -= m
|
|
}
|
|
}
|
|
|
|
const (
|
|
fastaLookupSize = 4096
|
|
fastaLookupScale float64 = fastaLookupSize - 1
|
|
)
|
|
|
|
var fastaRand uint32 = 42
|
|
|
|
type fastaAcid struct {
|
|
sym byte
|
|
prob float64
|
|
cprob float64
|
|
next *fastaAcid
|
|
}
|
|
|
|
func fastaComputeLookup(acid []fastaAcid) *[fastaLookupSize]*fastaAcid {
|
|
var lookup [fastaLookupSize]*fastaAcid
|
|
var p float64
|
|
for i := range acid {
|
|
p += acid[i].prob
|
|
acid[i].cprob = p * fastaLookupScale
|
|
if i > 0 {
|
|
acid[i-1].next = &acid[i]
|
|
}
|
|
}
|
|
acid[len(acid)-1].cprob = 1.0 * fastaLookupScale
|
|
|
|
j := 0
|
|
for i := range lookup {
|
|
for acid[j].cprob < float64(i) {
|
|
j++
|
|
}
|
|
lookup[i] = &acid[j]
|
|
}
|
|
|
|
return &lookup
|
|
}
|
|
|
|
func fastaRandom(out *fastaBuffer, acid []fastaAcid, n int) {
|
|
const (
|
|
IM = 139968
|
|
IA = 3877
|
|
IC = 29573
|
|
)
|
|
lookup := fastaComputeLookup(acid)
|
|
for n > 0 {
|
|
m := n
|
|
if m > fastaLine {
|
|
m = fastaLine
|
|
}
|
|
buf := out.NextWrite(m + 1)
|
|
f := fastaLookupScale / IM
|
|
myrand := fastaRand
|
|
for i := 0; i < m; i++ {
|
|
myrand = (myrand*IA + IC) % IM
|
|
r := float64(int(myrand)) * f
|
|
a := lookup[int(r)]
|
|
for a.cprob < r {
|
|
a = a.next
|
|
}
|
|
buf[i] = a.sym
|
|
}
|
|
fastaRand = myrand
|
|
buf[m] = '\n'
|
|
n -= m
|
|
}
|
|
}
|