From b7a2d413a3f710f14accedf185c93bfb63d24fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6hrmann?= Date: Tue, 20 Oct 2020 09:56:14 +0200 Subject: [PATCH] testing: print cpu type as label for benchmarks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Keith Randall --- src/go/build/deps_test.go | 6 +++- src/internal/cpu/cpu_no_name.go | 19 +++++++++++++ src/internal/cpu/cpu_x86.go | 49 +++++++++++++++++++++++++++++++++ src/internal/sysinfo/sysinfo.go | 31 +++++++++++++++++++++ src/testing/benchmark.go | 7 +++++ 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/internal/cpu/cpu_no_name.go create mode 100644 src/internal/sysinfo/sysinfo.go diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 4d866c87b6..ba7a76318f 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -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 diff --git a/src/internal/cpu/cpu_no_name.go b/src/internal/cpu/cpu_no_name.go new file mode 100644 index 0000000000..ce1c37a3c7 --- /dev/null +++ b/src/internal/cpu/cpu_no_name.go @@ -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 "" +} diff --git a/src/internal/cpu/cpu_x86.go b/src/internal/cpu/cpu_x86.go index da6cf67258..fb414adaf8 100644 --- a/src/internal/cpu/cpu_x86.go +++ b/src/internal/cpu/cpu_x86.go @@ -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 +} diff --git a/src/internal/sysinfo/sysinfo.go b/src/internal/sysinfo/sysinfo.go new file mode 100644 index 0000000000..961be7abae --- /dev/null +++ b/src/internal/sysinfo/sysinfo.go @@ -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 +} diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go index e9687bf26d..1b81ec3a2d 100644 --- a/src/testing/benchmark.go +++ b/src/testing/benchmark.go @@ -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)