在Go中,什么是深度复制切片的简洁/良好表现方式?我需要将切片复制到新的后备阵列,因为另一个阵列由其他内容拥有,并且可能在复制后进行修改.
我现在这样做:
copy := append([]T{}, orig...)
Run Code Online (Sandbox Code Playgroud)
T元素类型在哪里orig.
ANi*_*sus 12
没有基准测试,不确定哪种解决方案最快,但另一种选择是使用内置的copy:
cpy := make([]T, len(orig))
copy(cpy, orig)
Run Code Online (Sandbox Code Playgroud)
从文档:
func copy(dst,src [] Type)int
复制内置函数将元素从源切片复制到目标切片.(作为一种特殊情况,它还会将字符串中的字节复制到一个字节片段.)源和目标可能重叠.Copy返回复制的元素数,这将是len(src)和len(dst)的最小值.
注意
解决方案将复制切片中的所有值.如果切片包含带指针字段的指针或结构,则这些指针值仍将指向与orig切片相同的值.
基准
对这两个选项进行基准测试,您可以看到它们具有非常相似的性能.
BenchmarkCopy 100000 24724 ns/op
BenchmarkAppend 100000 24967 ns/op
ok benchmark 5.478s
Run Code Online (Sandbox Code Playgroud)
这是基准代码:
包主
import "testing"
var result []T
const size = 10000
type T int
func BenchmarkCopy(b *testing.B) {
orig := make([]T, size)
for n := 0; n < b.N; n++ {
cpy := make([]T, len(orig))
copy(cpy, orig)
orig = cpy
}
result = orig
}
func BenchmarkAppend(b *testing.B) {
orig := make([]T, size)
for n := 0; n < b.N; n++ {
cpy := append([]T{}, orig...)
orig = cpy
}
result = orig
}
Run Code Online (Sandbox Code Playgroud)
我不确定何时/是否执行了零填充.但是如果你看看程序集,在附加版本中你会得到:
CALL ,runtime.growslice(SB)
Run Code Online (Sandbox Code Playgroud)
而副本将致电:
CALL ,runtime.makeslice(SB)
Run Code Online (Sandbox Code Playgroud)
我猜这两个调用都执行零填充.
slicecopy := append([]T(nil), slice...)
Run Code Online (Sandbox Code Playgroud)
例如,
package main
import "fmt"
func main() {
type T int
slice := make([]T, 8)
for i := range slice {
slice[i] = T(i)
}
fmt.Println(len(slice), cap(slice), &slice[0], slice)
slicecopy := append([]T(nil), slice...)
fmt.Println(len(slicecopy), cap(slicecopy), &slicecopy[0], slicecopy)
}
Run Code Online (Sandbox Code Playgroud)
输出:
8 8 0x10322160 [0 1 2 3 4 5 6 7] 8 8 0x103221a0 [0 1 2 3 4 5 6 7]
参考:
// Make a copy of a slice (of int).
slice3 := append([]int(nil), slice...)
fmt.Println("Copy a slice:", slice3)
Run Code Online (Sandbox Code Playgroud)
基准:
package main
import "testing"
var result []T
const size = 1000
type T int
func BenchmarkCopy(b *testing.B) {
orig := make([]T, size)
for n := 0; n < b.N; n++ {
cpy := make([]T, len(orig))
copy(cpy, orig)
orig = cpy
}
result = orig
}
func BenchmarkAppend(b *testing.B) {
orig := make([]T, size)
for n := 0; n < b.N; n++ {
cpy := append([]T{}, orig...)
orig = cpy
}
result = orig
}
func BenchmarkAppendPreCapped(b *testing.B) {
orig := make([]T, size)
for n := 0; n < b.N; n++ {
cpy := append(make([]T, 0, len(orig)), orig...)
orig = cpy
}
result = orig
}
func BenchmarkAppendNil(b *testing.B) {
orig := make([]T, size)
for n := 0; n < b.N; n++ {
cpy := append([]T(nil), orig...)
orig = cpy
}
result = orig
}
func main() {}
Run Code Online (Sandbox Code Playgroud)
输出:
$ go version
go version devel +ffe33f1f1f17 Tue Nov 25 15:41:33 2014 +1100 linux/amd64
$ go test -v -bench=.
testing: warning: no tests to run
PASS
BenchmarkCopy 200000 9983 ns/op
BenchmarkAppend 200000 10004 ns/op
BenchmarkAppendPreCapped 200000 10077 ns/op
BenchmarkAppendNil 200000 9960 ns/op
ok so/test 8.412s
$ go test -v -bench=.
testing: warning: no tests to run
PASS
BenchmarkCopy 200000 10000 ns/op
BenchmarkAppend 200000 10112 ns/op
BenchmarkAppendPreCapped 200000 9892 ns/op
BenchmarkAppendNil 200000 10005 ns/op
ok so/test 8.422s
$ go test -v -bench=.
testing: warning: no tests to run
PASS
BenchmarkCopy 200000 9967 ns/op
BenchmarkAppend 200000 9898 ns/op
BenchmarkAppendPreCapped 200000 10123 ns/op
BenchmarkAppendNil 200000 10022 ns/op
ok so/test 8.424s
$
Run Code Online (Sandbox Code Playgroud)
最快的方法似乎是附加到具有必要空间的切片。我用基准测试结果扩展了@Anisus 答案,以及由此产生的最快解决方案。
BenchmarkCopy 100000 18240 ns/op
BenchmarkAppend 100000 18276 ns/op
BenchmarkAppendPreCapped 100000 16407 ns/op
Run Code Online (Sandbox Code Playgroud)
BenchmarkAppendPreCapped 可能会避免切片归零和/或增长。看起来像这样:
copy := append(make([]T, 0, len(orig)), orig...)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7604 次 |
| 最近记录: |