Repository: go
Issue: cmd/compile: suboptimal cloning/optimization in slices.Clone
Labels: Performance,NeedsDecision,compiler/runtime

### What version of Go are you using (`go version`)?

<pre>
$ go version
go version go1.18.3 linux/amd64
</pre>

### Does this issue reproduce with the latest release?

Yes with `go version devel go1.19-d3ffff2790 Tue Jun 28 13:01:41 2022 +0000 linux/amd64`.

### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ 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"
</pre></details>

### Discoveries / Proposal

`slices.Clone` is currently defined as:

```go
// 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...)
}
```

`append` here seems to be optimized to output what is essentially `make` + `copy`.  But if we do that explicitly:

```go
// 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
}
```

and benchmark it (see <https://go.dev/play/p/WHvD0zJ33S1>) then the new function generally performs better:

```none
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
```

Looking at the assembly, it seems that the current version uses `runtime.growslice` while the new one, `runtime.mallocgc`.  There are other changes in the assembly as well.