This is a creation in Article, where the information may have evolved or changed.
Copyright: The Go Authors. All rights reserved.//Use of this source code are governed by a bsd-style//license so can be found in the license file. Package Testingimport ("Flag" "FMT" "Internal/race" "OS" "Runtime" "Sync" "sync/atomic" "Time") var Matchbenchmarks = flag. String ("Test.bench", "", "run only benchmarks matching ' regexp '") var benchtime = flag. Duration ("Test.benchtime", 1*time. Second, "run each benchmark for duration ' d '") var benchmarkmemory = flag. Bool ("Test.benchmem", False, "print memory allocations for benchmarks")//Global lock to ensure-one benchmark runs at A Time.var benchmarklock sync. mutex//used for every benchmark for measuring memory.var memstats runtime. memstats//an internal type but exported because it is cross-package; Part of the implementation//of the ' Go test ' command.type internalbenchmark struct {Name stringf func (b *b)}//B is a Type passed to Benchmark functions to manage benchmark//timing and to specify the number of iterations to run.////A benchmark ends when it benchmark function returns or calls any of the methods//Failnow, Fatal, Fatalf, Skipnow, Skip, or SKIPF. Those methods must is called//only from the goroutine running the Benchmark function.//the other reporting methods, such As the variations of Log and error,//May is called simultaneously from multiple goroutines.////like in tests, benchmark Logs is accumulated during execution//and dumped to standard error when done. Unlike in tests, benchmark logs//is always printed, so as not to hide output whose existence may be//affecting Benchmar K Results.type B struct {commonimportpath string//import path of the package containing the Benchmarkcontext *benchcontextn INTPREVIOUSN Int//number of iterations in the previous Runpreviousdur ation time. Duration//Total Duration of the previous runbenchfunc func (b *b) benchtime time. Durationbytes int64missingbytes BOOL//One of the SubbeNchmarks does not having bytes Set.timeron Boolshowallocresult boolresult benchmarkresultparallelism int//Runparallel creates Parallelism*gomaxprocs goroutines//the initial states of Memstats.mallocs and Memstats.total Alloc.startallocs Uint64startbytes uint64//The net total of this test after being Run.netallocs uint64netbytes uint64}/ /Starttimer starts timing a test. This function was called automatically//before a benchmark starts, but it can also used to resume timing after//a call to Stoptimer.func (b *b) Starttimer () {if!b.timeron {runtime. Readmemstats (&memstats) B.startallocs = MemStats.Mallocsb.startBytes = MemStats.TotalAllocb.start = time. Now () B.timeron = true}}//Stoptimer stops timing a test. This can is used to pause the timer//while performing complex initialization so you don ' t//want to Measure.func (b *b) Stoptimer () {if B.timeron {b.duration + = time. Now (). Sub (B.start) runtime. Readmemstats (&memstats) B.netallocs + = Memstats.mallocS-b.startallocsb.netbytes + = Memstats.totalalloc-b.startbytesb.timeron = false}}//ResetTimer Zeros the elapsed benchm Ark Time and memory allocation counters.//IT does not affect whether the timer is Running.func (b *b) Resettimer () {if B. timerOn {runtime. Readmemstats (&memstats) B.startallocs = MemStats.Mallocsb.startBytes = MemStats.TotalAllocb.start = time. Now ()}b.duration = 0b.netallocs = 0b.netbytes = 0}//SetBytes records the number of bytes processed in a single operation. If This is called, the benchmark would report Ns/op and Mb/s.func (b *b) setbytes (n Int64) {b.bytes = n}//reportalloc s enables malloc statistics for this benchmark.//it's equivalent to Setting-test.benchmem, but it's only affects the//be Nchmark function that calls Reportallocs.func (b *b) Reportallocs () {b.showallocresult = True}func (b *b) Nsperop () Int64 {if B.N <= 0 {return 0}return b.duration.nanoseconds ()/Int64 (B.N)}//Runn runs a single benchmark for the specified N Umber of ITERATIONS.FUNC (b *b) Runn (n int) {Benchmarklock.lock () defer benchmarklock.unlock ()//Try to get a comparable environment for each RU n//by clearing garbage from previous runs.runtime.GC () B.raceerrors =-race. Errors () B.N = Nb.parallelism = 1b. Resettimer () B.starttimer () B.benchfunc (b) b.stoptimer () B.previousn = Nb.previousduration = b.durationb.raceerrors + = Race. Errors () If b.raceerrors > 0 {B.errorf ("race detected during execution of benchmark")}}func min (x, y int) int {if x > Y {return y}return x}func max (x, y int) int {if x < y {return Y}return x}//roundDown10 rounds a number down to the NE Arest Power of 10.func roundDown10 (n int) int {var Tens = 0//Tens = Floor (LOG_10 (n)) for n >= ten {n = n/10tens++}//r Esult = 10^tensresult: = 1for I: = 0; I < Tens; i++ {result *= 10}return result}//roundUp rounds x up to a number of the form [1eX, 2eX, 3eX, 5ex].func roundUp (n int.) in t {base: = ROUNDDOWN10 (n) switch {case n <= base:return basecase n <= (2 * Base): Return 2 * basecase N <= (3 * Base): Return 3 * basecase n <= (5 * Base): return 5 * Basedefault:return * base}}//run1 runs the first ite Ration of Benchfunc. It returns whether more//iterations of this benchmarks should be Run.func (b *b) run1 () bool {if ctx: = B.context; CTX! = Nil {//Extend maxlen, if needed.if n: = Len (b.name) + Ctx.extlen + 1; n > Ctx.maxlen {ctx.maxlen = n + 8//ADD Addit ional slack to avoid too many jumps in size.}} Go func () {//Signal that we ' re do whether we return normally//or by Failnow ' s runtime. Goexit.defer func () {b.signal <-true} () B.runn (1)} () <-b.signalif b.failed {fmt. fprintf (B.W, "---FAIL:%s\n%s", B.name, B.output) return false}//only print the output if we know we is not going to pro ceed.//Otherwise It is printed in Processbench.if Atomic. LoadInt32 (&b.hassub)! = 0 | | b.finished {tag: = "BENCH" if b.skipped {tag = "SKIP"}if b.chatty && (len (b.output) > 0 | | b.finished) {B.trimou Tput () fmt. fprintf (B.W, "---%s:%s\n%s", Tag, B.name, B.OUtput)}return false}return true}var labelsonce sync. once//run executes the benchmark in a separate goroutine, including all of its//subbenchmarks. B must not has Subbenchmarks.func (b *b) run () Benchmarkresult {labelsonce.do (func () {FMT). fprintf (B.W, "GOOS:%s\n", runtime.) GOOS) fmt. fprintf (B.W, "Goarch:%s\n", runtime.) Goarch) if B.importpath! = "" {fmt. fprintf (B.W, "pkg:%s\n", B.importpath)}) if B.context! = nil {//Running go Test--test.benchb.context.processbench (b)// Must call Dobench.} else {//Running func benchmark.b.dobench ()}return B.result}func (b *b) Dobench () Benchmarkresult {go b.launch () <-b.si Gnalreturn b.result}//launch launches the benchmark function. It gradually increases the number//of benchmark iterations until the benchmark runs for the requested benchtime.//launch is run by the Dobench function as a separate goroutine.//run1 must has been called on B.func (b *b) launch () {//Signal That we ' re do whether we return normally//or by Failnow ' s runtime. GoexIt.defer func () {b.signal <-true} ()//Run The benchmark for at least the specified amount of TIME.D: = B.benchtimefor N: = 1;!b.failed && B.duration < D && N < 1e9; {Last: = n//Predict Required ITERATIONS.N = Int (d.nanoseconds ()) if nsop: = B.nsperop (); Nsop! = 0 {n/= int (nsop)}//Run More iterations than we think we ' ll need (1.2X).//Don ' t grow too fast in case we had timing errors previously.//be sure To run at least one more than last TIME.N = max (min (N+N/5, 100*last), last+1)//Round up to something easy to READ.N = Rou Ndup (n) B.runn (n)}b.result = BENCHMARKRESULT{B.N, B.duration, B.bytes, B.netallocs, b.netbytes}}//the results of a benchm Ark Run.Type benchmarkresult struct {N int//The number of iterations. T time. Duration//The total time taken. Bytes Int64//Bytes processed in one iteration. Memallocs UInt64//The total number of memory allocations. Membytes UInt64//The total number of bytes AllocAted.} Func (R Benchmarkresult) Nsperop () Int64 {if R.N <= 0 {return 0}return r.t.nanoseconds ()/Int64 (R.N)}func (R Benchmark Result) mbpersec () float64 {if r.bytes <= 0 | | r.t <= 0 | | R.N <= 0 {return 0}return (float64 (r.bytes) * Float64 ( R.N)/1e6)/R.t.seconds ()}//Allocsperop returns R.memallocs/r.n.func (R Benchmarkresult) Allocsperop () Int64 {if R.N <= 0 {return 0}return int64 (R.memallocs)/Int64 (R.N)}//Allocedbytesperop returns R.membytes/r.n.func (R Benchmarkre Sult) Allocedbytesperop () Int64 {if R.N <= 0 {return 0}return int64 (r.membytes)/Int64 (R.N)}func (R Benchmarkresult) S Tring () string {mbs: = R.mbpersec () MB: = "" If MBS! = 0 {mb = fmt. Sprintf ("\t%7.2f MB/S", MBS)}nsop: = R.nsperop () ns: = FMT. Sprintf ("%10d ns/op", Nsop) if R.N > 0 && Nsop < +//The format specifiers here make sure that//the ones Digits line up for all three possible formats.if Nsop < {ns = FMT. Sprintf ("%13.2f ns/op", Float64 (R.t.nanoseconds ())/float64 (R.N))}else {ns = FMT. Sprintf ("%12.1f ns/op", Float64 (R.t.nanoseconds ())/float64 (R.N))}}return FMT. Sprintf ("%8d\t%s%s", R.N, NS, MB)}//memstring returns R.allocedbytesperop and R.allocsperop in the same format as ' Go tes T '. Func (R Benchmarkresult) memstring () string {return FMT. Sprintf ("%8d b/op\t%8d allocs/op", R.allocedbytesperop (), R.allocsperop ())}//Benchmarkname returns full name of Benchmark including procs Suffix.func benchmarkname (name string, n int) string {if n! = 1 {return FMT. Sprintf ("%s-%d", Name, N)}return name}type benchcontext struct {match *matchermaxlen int//The largest recorded benchmark Name.extlen int//Maximum extension length.} An internal function and exported because it is cross-package; Part of the implementation//of the "Go Test" Command.func runbenchmarks (matchstring func (Pat, str string) (bool, error), Benchmarks []internalbenchmark] {Runbenchmarks ("", matchstring, benchmarks)}func Runbenchmarks (ImportPath string, matchstring func (Pat, str string) (bool, ERROR), benchmarks []internalbenchmark) bool {//IF no flag was specified, don ' t run Benchmarks.if len (*matchbenchmarks) = = 0 {return true}//Collect matching benchmarks and determine longest name.maxprocs: = 1for _, procs: = Range Cpulist {if PR OCS > Maxprocs {maxprocs = Procs}}ctx: = &benchcontext{match:newmatcher (matchstring, *matchbenchmarks, "-test.be Nch "), Extlen:len (Benchmarkname (" ", Maxprocs)),}var BS []internalbenchmarkfor _, Benchmark: = Range Benchmarks {If _, MATC Hed, _: = Ctx.match.fullName (nil, benchmark.name); matched {bs = append (BS, Benchmark) Benchname: = Benchmarkname (Benchmark.name, Maxprocs) if L: = Len (benchname) + Ctx.extlen + 1; L > Ctx.maxlen {ctx.maxlen = L}}}main: = &b{common:common{name: "Main", W:os. Stdout,chatty: *chatty,},importpath:importpath,benchfunc:func (b *b) {for _, Benchmark: = range bs {b.run (benchmark.name , Benchmark.f)}},benchtime: *benchtime,context:ctx,}main.runn (1) return!main.failed}//Processbench runs bench b for tHe configured CPU counts and prints the Results.func (ctx *benchcontext) processbench (b *b) {For I, procs: = Range cpulist {Runtime. Gomaxprocs (procs) Benchname: = Benchmarkname (B.name, procs) fmt. fprintf (B.W, "%-*s\t", Ctx.maxlen, Benchname)//Recompute The running time for all but the first iteration.if i > 0 {b = &b{common:common{signal:make (chan bool), Name:b.name,w:b.w,chatty:b.chatty,},benchfunc:b.benchfunc,bench TIME:B.BENCHTIME,}B.RUN1 ()}r: = B.dobench () if b.failed {//The output could is very long here, but probably isn ' t.//We p Rint it all, regardless, because we don't want to trim the reason//the benchmark failed.fmt.Fprintf (B.W, "---FAIL:%s\n% S ", Benchname, b.output) Continue}results: = R.string () If *benchmarkmemory | | B.showallocresult {results + = "\ T" + r.memstring ()}fmt. Fprintln (B.W, results)//Unlike with tests, we ignore The-chatty flag and always print output for//benchmarks Output generation time would skew the Results.if Len (b.output) > 0 {b.trimoutput () fmt. fprintf (B.W, "---BENCH:%s\n%s", Benchname, B.output)}if p: = runtime. Gomaxprocs (-1); P! = procs {fmt. fprintf (OS. Stderr, "Testing:%s left Gomaxprocs set to%d\n", Benchname, p)}}}//Run benchmarks F as a subbenchmark with the given NA Me. It reports//whether there were any failures.////A Subbenchmark are like any other benchmark. A benchmark that calls run at//least once would not be measured itself and would be called once with n=1.////run may CA lled simultaneously from multiple goroutines, but all such//calls must return before the outer benchmark function for B R Eturns.func (b *b) run (name string, f func (b *b)) bool {//Since B has subbenchmarks, we'll no longer Run it as a benchm Ark itself.//Release The lock and acquire it on exit to ensure locks stay paired.atomic.StoreInt32 (&b.hassub, 1) Bench Marklock.unlock () defer benchmarklock.lock () benchname, OK, partial: = B.name, True, falseif b.context! = Nil {benchname, OK , partial = B.context.match.fullName (&b.common, name)}if!ok {return true}sub: = &b{common:common{signal:make (chan bool), Name:benchname,paren T: &b.common,level:b.level + 1,w:b.w,chatty:b.chatty,},importpath:b.importpath,benchfunc:f,benchtime:b.b Enchtime,context:b.context,}if Partial {//partial name match, like-bench=x/y matching benchmarkx.//only process sub -benchmarks, if Any.atomic.StoreInt32 (&sub.hassub, 1)}if sub.run1 () {Sub.run ()}b.add (Sub.result) return! sub.failed}//add simulates running benchmarks in sequence in a single iteration. It is//used to give some meaningful results in case func Benchmark was used in//combination with Run.func (b *b) Add (othe R Benchmarkresult) {r: = &b.result//The aggregated benchmarkresults resemble running all Subbenchmarks as//in Sequen Ce in a single BENCHMARK.R.N = 1r. T + = time. Duration (Other. Nsperop ()) if other. Bytes = = 0 {//summing Bytes is meaningless in aggregate if not all subbenchmarks//set it.b.missingbytes = Truer. Bytes =0}if!b.missingbytes {r.bytes + = other. Bytes}r.memallocs + = UInt64 (other. Allocsperop ()) R.membytes + = UInt64 (other. Allocedbytesperop ())}//Trimoutput shortens the output from a benchmark, which can be very long.func (b *b) Trimoutput () { The output is likely to appear multiple times because the benchmark//are run multiple times, but at least it would be SE En. This isn't a big deal//because benchmarks rarely print, but just in case, we trim it ' s too long.const maxnewlines = 10for Nlcount, J: = 0, 0; J < Len (b.output); J + + {if b.output[j] = = ' \ n ' {nlcount++if nlcount >= maxnewlines {b.output = append (B.output[:j], "\n\t ... [Output truncated]\n ...] break}}}}//A PB is used by Runparallel for running parallel benchmarks.type PB struct {globaln *uint64//shared between All worker Goroutines Iteration Countergrain UInt64//Acquire, many iterations from Globaln at Oncecache UInt64 Local cache of acquired ITERATIONSBN UInt64//Total number of iterations to EXecute (B.N)}//Next reports whether there is more iterations to Execute.func (PB *PB) Next () bool {if Pb.cache = = 0 {N: = Atomic. AddUint64 (PB.GLOBALN, Pb.grain) if n <= pb.bn {pb.cache = pb.grain} else if n < pb.bn+pb.grain {Pb.cache = pb.bn + PB . grain-n} else {return False}}pb.cache--return true}//Runparallel runs a benchmark in parallel.//It creates multiple g Oroutines and distributes B.N iterations among them.//the number of goroutines defaults to Gomaxprocs. To increase parallelism for//non-cpu-bound benchmarks, call Setparallelism before runparallel.//runparallel is usually u Sed with the go test-cpu flag.////the body function would be is run in each goroutine. It should set up any//goroutine-local, and then iterate until PB. Next returns false.//It should not use the Starttimer, Stoptimer, or Resettimer functions,//because they has global eff Ect. It should also not call Run.func (b *b) Runparallel (body func (*PB)) {if B.N = = 0 {return//Nothing to do when ProbiNg.} Calculate grain size As number of iterations that take ~100µs.//100µs are enough to amortize the overhead and provide s ufficient//dynamic load Balancing.grain: = UInt64 (0) if B.previousn > 0 && b.previousduration > 0 {grain = 1 e5 * UInt64 (B.PREVIOUSN)/UInt64 (b.previousduration)}if Grain < 1 {grain = 1}//We Expect the inner loop and function Call-to-take at least 10ns,//so does not does more than 100µs/10ns=1e4 Iterations.if grain > 1e4 {grain = 1e4}n: = UInt64 ( 0) Numprocs: = B.parallelism * runtime. Gomaxprocs (0) var wg sync. Waitgroupwg.add (Numprocs) for P: = 0; P < Numprocs; p++ {go func () {defer WG. Done () PB: = &pb{globaln: &n,grain:grain,bn:uint64 (B.N),}body (Pb)} ()}wg. Wait () if n <= uint64 (B.N) &&!b.failed () {b.fatal ("Runparallel:body exited without PB. Next () = = False ")}}//Setparallelism sets the number of goroutines used by Runparallel to p*gomaxprocs.//there is usually No need to call setparallelism for Cpu-bound benchmarks.//if P is less than 1, this call would have no Effect.func (b *b) setparallelism (p int) {If P >= 1 {b.parallelism = p}}/ /Benchmark benchmarks a single function. Useful for creating//custom benchmarks the "Go test" command.////If F calls Run, the result would be a E Stimate of running all its//subbenchmarks this don ' t call Run in sequence in a single Benchmark.func benchmark (f func (b * b)) Benchmarkresult {b: = &b{common:common{signal:make (chan bool), W:discard{},},benchfunc:f,benchtime: *bench Time,}if b.run1 () {b.run ()}return b.result}type discard Struct{}func (discard) Write (b []byte) (n int, err error) {return Len (b), nil}
The above refers to the go source, benchmark part;
Initialization of the 1,benchmark:
> (1) The test cycle of default one second can be set by itself; other reference flags
> (2), constant: 1e9 Max loop
> (3), benchmarkxxx function of the stack mode