Go版本
go版本 devel go1.23-5e1e3a0025 Thu Mar 21 17:25:54 2024 +0000 linux/amd64
在你的模块/工作区中运行go env
的输出结果:
GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/rogpeppe/.cache/go-build'
GOENV='/home/rogpeppe/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/rogpeppe/src/go/pkg/mod'
GOOS='linux'
GOPATH='/home/rogpeppe/src/go'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/rogpeppe/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/home/rogpeppe/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='devel go1.23-5e1e3a0025 Thu Mar 21 17:25:54 2024 +0000'
GODEBUG=''
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/rogpeppe/go/src/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2835753179=/tmp/go-build -gno-record-gcc-switches'
你做了什么?
我运行了这个测试脚本:
exec go run main1.go
cp stdout result1
exec go run main2.go
cp stdout result2
grep '1 allocs/op' result1
grep '1 allocs/op' result2
cmp stdout want-stdout
-- go.mod --
module m
-- main1.go --
// This version uses an extra layer of iteration.
package main
import (
"fmt"
"testing"
)
func main() {
r := testing.Benchmark(func(b *testing.B) {
err := errorT{"a"}
for range b.N {
if !AsType[errorT](err) {
panic("not ok")
}
}
})
fmt.Printf("BenchmarkAsType1: %v %v\n", r, r.MemString())
}
func AsType[T any](err error) bool {
for _ = range AllAs[T](err) {
return true
}
return false
}
func AllAs[T any](err error) func(func(T) bool) {
return func(yield func(T) bool) {
for err := range All(err) {
if x, ok := err.(T); ok {
if !yield(x) {
return
}
}
}
}
}
func All(err error) func(func(error) bool) {
return func(yield func(error) bool) {
yield(err)
}
}
type errorT struct{ s string }
func (e errorT) Error() string { return fmt.Sprintf("errorT(%s)", e.s) }
-- main2.go --
// This version avoids the intermediate iterator.
package main
import (
"fmt"
"testing"
)
func main() {
r := testing.Benchmark(func(b *testing.B) {
err := errorT{"a"}
for range b.N {
if !AsType[errorT](err) {
panic("not ok")
}
}
})
fmt.Printf("BenchmarkAsType2: %v %v\n", r, r.MemString())
}
func AsType[T any](err error) bool {
for err := range All(err) {
if _, ok := err.(T); ok {
return true
}
}
return false
}
func All(err error) func(func(error) bool) {
return func(yield func(error) bool) {
yield(err)
}
}
type errorT struct{ s string }
func (e errorT) Error() string { return fmt.Sprintf("errorT(%s)", e.s) }
你看到了什么?
> exec go run main1.go
[stdout]
BenchmarkAsType1: 5617491 199.9 ns/op 168 B/op 9 allocs/op
> cp stdout result1
> exec go run main2.go
[stdout]
BenchmarkAsType2: 31012682 32.98 ns/op 16 B/op 1 allocs/op
> cp stdout result2
> grep '1 allocs/op' result1
[result1]
BenchmarkAsType1: 5617491 199.9 ns/op 168 B/op 9 allocs/op
FAIL: /tmp/testscript4012864748/x.txtar/script.txtar:5: no match for `1 allocs/op` found in result1
error running /tmp/x.txtar in /tmp/testscript4012864748/x.txtar
你期望看到什么?
我期望这两个版本具有大致相似的性能特性。
它们都在做完全相同的基本工作,但其中一个比另一个快大约6倍。
也许在足够的编译器智能的情况下可以减少额外的分配。
顺便说一下,这个问题是在探索#66455实现时遇到的。
2条答案
按热度按时间irtuqstp1#
@dr2chase @mdempsky
js5cn81o2#
为了记录,这似乎与通用类型在这里发挥作用的事实无关。我将
AsType
更改为非通用,在两个示例中结果相同。