go ``` cmd/compile: 切片中存在次优的克隆/优化, ```

f5emj3cl  于 5个月前  发布在  Go
关注(0)|答案(4)|浏览(74)

你正在使用哪个版本的Go( go version )?

$ go version
go version go1.18.3 linux/amd64

这个问题在最新版本中是否会重现?

是的,在 go version devel go1.19-d3ffff2790 Tue Jun 28 13:01:41 2022 +0000 linux/amd64 中会重现。

你正在使用什么操作系统和处理器架构( go env )?

go env 输出

$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/ainar/.cache/go-build"
GOENV="/home/ainar/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/ainar/go/pkg/mod"
GONOPROXY="REMOVED"
GONOSUMDB="REMOVED"
GOOS="linux"
GOPATH="/home/ainar/go"
GOPRIVATE="REMOVED"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/ainar/go/go1.18"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/ainar/go/go1.18/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.18.3"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1722739114=/tmp/go-build -gno-record-gcc-switches"

发现/建议

slices.Clone 目前被定义为:

// Clone returns a copy of the slice.
// The elements are copied using assignment, so this is a shallow clone.
func Clone[S ~[]E, E any](s S) S {
	// Preserve nil in case it matters.
	if s == nil {
		return nil
	}
	return append(S([]E{}), s...)
}

在这里似乎优化了输出本质上是 make + copy 的内容。但是如果我们明确地这样做:

// Clone returns a copy of the slice.
// The elements are copied using assignment, so this is a shallow clone.
func Clone[S ~[]E, E any](s S) (clone S) {
	// Preserve nil in case it matters.
	if s == nil {
		return nil
	}
	clone = make(E, len(s))
	copy(clone, s)
	return clone
}

然后对其进行基准测试(参见 https://go.dev/play/p/WHvD0zJ33S1 ),那么新的函数通常表现得更好:

goos: linux
goarch: amd64
cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
BenchmarkFoo/10/clone_std-16            11532111                95.56 ns/op           80 B/op        1 allocs/op
BenchmarkFoo/10/clone_our-16            16797295                70.03 ns/op           80 B/op        1 allocs/op
BenchmarkFoo/100/clone_std-16            2828976               425.5 ns/op           896 B/op        1 allocs/op
BenchmarkFoo/100/clone_our-16            3188673               370.0 ns/op           896 B/op        1 allocs/op
BenchmarkFoo/1000/clone_std-16            369156              3092 ns/op            8192 B/op        1 allocs/op
BenchmarkFoo/1000/clone_our-16            407413              2963 ns/op            8192 B/op        1 allocs/op
BenchmarkFoo/10000/clone_std-16            49088             23761 ns/op           81920 B/op        1 allocs/op
BenchmarkFoo/10000/clone_our-16            51192             23345 ns/op           81920 B/op        1 allocs/op
BenchmarkFoo/100000/clone_std-16            3909            267590 ns/op          802821 B/op        1 allocs/op
BenchmarkFoo/100000/clone_our-16            4609            255535 ns/op          802822 B/op        1 allocs/op
BenchmarkFoo/1000000/clone_std-16            879           1197480 ns/op         8003595 B/op        1 allocs/op
BenchmarkFoo/1000000/clone_our-16            918           1186598 ns/op         8003594 B/op        1 allocs/op
PASS
ok      command-line-arguments  15.665s

从汇编代码来看,当前版本使用的是 runtime.growslice ,而新版本使用的是 runtime.mallocgc 。汇编代码中还有其他一些变化。

rslzwgfq

rslzwgfq1#

我相信我们使用的是 append 版本,这样我们可以让运行时向上取整到下一个大小类。这将允许调用者在不立即触发重新分配和复制的情况下向结果追加一些项目。
append 版本也知道它不需要在用副本覆盖分配之前将其归零。
考虑到这些,如果 append 版本不是更慢就好了。看起来对于大数组来说并不是这样,但对于小数组还是有一些开销的。

j7dteeu8

j7dteeu82#

如果这里有什么需要修复的,那就是在编译器里,所以改个标题。

ozxc1zmp

ozxc1zmp3#

追加版本也知道在用副本覆盖它之前不需要将分配值归零。
对于make+copy也是如此:https://go-review.googlesource.com/c/go/+/146719
我认为我们应该做的是检测append(X, s...)的情况,其中X在编译器中已知具有长度和容量为0,并将其重写为makeextended+copy,其中makeextended是一种make变体,可以像append一样计算新的切片大小,但可以假设比通用append情况更多的约束。这应该比make+copy稍慢。优化是否能显著提高速度以证明增加的复杂性需要进行基准测试。

twh00eeo

twh00eeo4#

我的两分钱建议是编写可读的库代码,而不是"酷炫的一行代码",即选项2(make+copy)是一个更好的实现方式,可以更快地阅读 - 而且碰巧更快,所以...太棒了!

相关问题