简洁深度复制一片?

Mat*_*ner 10 deep-copy go

在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)

我猜这两个调用都执行零填充.


pet*_*rSO 6

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)


Mat*_*ner 4

最快的方法似乎是附加到具有必要空间的切片。我用基准测试结果扩展了@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)

  • 我无法重现您的基准测试结果。请发布您的基准代码。 (2认同)