testing: print cpu type as label for benchmarks

Supports 386 and amd64 architectures on all operating systems.

Example output:
$ go test -bench=.*
goos: darwin
goarch: amd64
pkg: strconv
cpu: Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz
BenchmarkAtof64Decimal-4        	24431032	        46.8 ns/op
...

As the displayed CPU information is only used for information
purposes it is lazily initialized when needed using the new
internal/sysinfo package.

This allows internal/cpu to stay without dependencies and avoid
initialization costs when the CPU information is not needed as
the new code to query the CPU name in internal/cpu can be
dead code eliminated if not used.

Fixes #39214

Change-Id: I77ae5c5d2fed6b28fa78dd45075f9f0a6a7f1bfd
Reviewed-on: https://go-review.googlesource.com/c/go/+/263804
Trust: Martin Möhrmann <moehrmann@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Martin Möhrmann 2020-10-20 09:56:14 +02:00
parent 80182d45b5
commit b7a2d413a3
5 changed files with 111 additions and 1 deletions

View File

@ -467,6 +467,10 @@ var depsRules = `
< net/rpc
< net/rpc/jsonrpc;
# System Information
internal/cpu, sync
< internal/sysinfo;
# Test-only
log
< testing/iotest
@ -475,7 +479,7 @@ var depsRules = `
FMT, flag, math/rand
< testing/quick;
FMT, flag, runtime/debug, runtime/trace
FMT, flag, runtime/debug, runtime/trace, internal/sysinfo
< testing;
internal/testlog, runtime/pprof, regexp

View File

@ -0,0 +1,19 @@
// Copyright 2020 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.
// +build !386
// +build !amd64
package cpu
// Name returns the CPU name given by the vendor
// if it can be read directly from memory or by CPU instructions.
// If the CPU name can not be determined an empty string is returned.
//
// Implementations that use the Operating System (e.g. sysctl or /sys/)
// to gather CPU information for display should be placed in internal/sysinfo.
func Name() string {
// "A CPU has no name".
return ""
}

View File

@ -38,6 +38,8 @@ const (
cpuid_ADX = 1 << 19
)
var maxExtendedFunctionInformation uint32
func doinit() {
options = []option{
{Name: "adx", Feature: &X86.HasADX},
@ -65,6 +67,8 @@ func doinit() {
return
}
maxExtendedFunctionInformation, _, _, _ = cpuid(0x80000000, 0)
_, _, ecx1, edx1 := cpuid(1, 0)
X86.HasSSE2 = isSet(edx1, cpuid_SSE2)
@ -103,3 +107,48 @@ func doinit() {
func isSet(hwc uint32, value uint32) bool {
return hwc&value != 0
}
// Name returns the CPU name given by the vendor.
// If the CPU name can not be determined an
// empty string is returned.
func Name() string {
if maxExtendedFunctionInformation < 0x80000004 {
return ""
}
data := make([]byte, 0, 3*4*4)
var eax, ebx, ecx, edx uint32
eax, ebx, ecx, edx = cpuid(0x80000002, 0)
data = appendBytes(data, eax, ebx, ecx, edx)
eax, ebx, ecx, edx = cpuid(0x80000003, 0)
data = appendBytes(data, eax, ebx, ecx, edx)
eax, ebx, ecx, edx = cpuid(0x80000004, 0)
data = appendBytes(data, eax, ebx, ecx, edx)
// Trim leading spaces.
for len(data) > 0 && data[0] == ' ' {
data = data[1:]
}
// Trim tail after and including the first null byte.
for i, c := range data {
if c == '\x00' {
data = data[:i]
break
}
}
return string(data)
}
func appendBytes(b []byte, args ...uint32) []byte {
for _, arg := range args {
b = append(b,
byte((arg >> 0)),
byte((arg >> 8)),
byte((arg >> 16)),
byte((arg >> 24)))
}
return b
}

View File

@ -0,0 +1,31 @@
// Copyright 2020 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 sysinfo implements high level hardware information gathering
// that can be used for debugging or information purposes.
package sysinfo
import (
internalcpu "internal/cpu"
"sync"
)
type cpuInfo struct {
once sync.Once
name string
}
var CPU cpuInfo
func (cpu *cpuInfo) Name() string {
cpu.once.Do(func() {
// Try to get the information from internal/cpu.
if name := internalcpu.Name(); name != "" {
cpu.name = name
return
}
// TODO(martisch): use /proc/cpuinfo and /sys/devices/system/cpu/ on Linux as fallback.
})
return cpu.name
}

View File

@ -8,6 +8,7 @@ import (
"flag"
"fmt"
"internal/race"
"internal/sysinfo"
"io"
"math"
"os"
@ -262,6 +263,9 @@ func (b *B) run() {
if b.importPath != "" {
fmt.Fprintf(b.w, "pkg: %s\n", b.importPath)
}
if cpu := sysinfo.CPU.Name(); cpu != "" {
fmt.Fprintf(b.w, "cpu: %s\n", cpu)
}
})
if b.context != nil {
// Running go test --test.bench
@ -648,6 +652,9 @@ func (b *B) Run(name string, f func(b *B)) bool {
if b.importPath != "" {
fmt.Printf("pkg: %s\n", b.importPath)
}
if cpu := sysinfo.CPU.Name(); cpu != "" {
fmt.Printf("cpu: %s\n", cpu)
}
})
fmt.Println(benchName)