]> Cypherpunks repositories - gostls13.git/commitdiff
testing: print cpu type as label for benchmarks
authorMartin Möhrmann <moehrmann@google.com>
Tue, 20 Oct 2020 07:56:14 +0000 (09:56 +0200)
committerMartin Möhrmann <moehrmann@google.com>
Tue, 20 Oct 2020 21:30:18 +0000 (21:30 +0000)
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>
src/go/build/deps_test.go
src/internal/cpu/cpu_no_name.go [new file with mode: 0644]
src/internal/cpu/cpu_x86.go
src/internal/sysinfo/sysinfo.go [new file with mode: 0644]
src/testing/benchmark.go

index 4d866c87b62ad199f8f21a335537ec0da3e03345..ba7a76318fc98d55b84d8bdd81dde31f31e04f0f 100644 (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
diff --git a/src/internal/cpu/cpu_no_name.go b/src/internal/cpu/cpu_no_name.go
new file mode 100644 (file)
index 0000000..ce1c37a
--- /dev/null
@@ -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 ""
+}
index da6cf67258190ddc51c02a7499a65dbfb9b2e6ba..fb414adaf84440864142659baf7d9e8e4bafdf09 100644 (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
+}
diff --git a/src/internal/sysinfo/sysinfo.go b/src/internal/sysinfo/sysinfo.go
new file mode 100644 (file)
index 0000000..961be7a
--- /dev/null
@@ -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
+}
index e9687bf26def25a3b91beebda27529a507cf3e22..1b81ec3a2d61610e0e3e7ff2d03eae61bd31248d 100644 (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)