简洁深度复制一片?

时间:2014-11-21 06:45:20

标签: go deep-copy

在Go中,什么是深度复制切片的简洁/良好表现方式?我需要将切片复制到新的支持数组,因为另一个数组由其他数组拥有,并且可能在复制后被修改。

我目前正在这样做:

copy := append([]T{}, orig...)

其中Torig的元素类型。

3 个答案:

答案 0 :(得分:11)

如果没有基准测试,不确定哪种解决方案最快,但另一种选择是使用内置的copy

cpy := make([]T, len(orig))
copy(cpy, orig)

来自the documentation

  

func copy(dst,src [] Type)int

     

复制内置函数将元素从源切片复制到   目的地切片。 (作为一种特殊情况,它也会从a复制字节   字符串到一片字节。)源和目标可能重叠。   复制返回复制的元素数,这将是最小值   len(src)和len(dst)。

注意

解决方案将复制切片中的所有值。如果切片包含带有指针字段的指针或结构,则这些指针值仍将指向与orig切片相同的值。

<强>基准

对两个选项进行基准测试,您可以看到它们具有非常相似的性能。

BenchmarkCopy     100000         24724 ns/op
BenchmarkAppend   100000         24967 ns/op
ok      benchmark   5.478s

这是基准代码:

package main

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
}

我不确定何时/是否执行了零填充。但是如果你看看程序集,在附加版本中你会得到:

CALL    ,runtime.growslice(SB)

副本将调用:

CALL    ,runtime.makeslice(SB)

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

答案 1 :(得分:1)

似乎最快的方法是附加到具有必要空间的切片。我已经用基准测试结果扩展了@Anisus的答案,以及最快的解决方案。

BenchmarkCopy            100000 18240 ns/op
BenchmarkAppend          100000 18276 ns/op
BenchmarkAppendPreCapped 100000 16407 ns/op

BenchmarkAppendPreCapped可能会避免切片归零和/或增长。它看起来像这样:

copy := append(make([]T, 0, len(orig)), orig...)

答案 2 :(得分:0)

slicecopy := append([]T(nil), slice...)

例如,

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

输出:

8 8 0x10322160 [0 1 2 3 4 5 6 7]
8 8 0x103221a0 [0 1 2 3 4 5 6 7]

参考文献:

Arrays, slices (and strings): The mechanics of 'append'

// Make a copy of a slice (of int).
slice3 := append([]int(nil), slice...)
fmt.Println("Copy a slice:", slice3)

基准:

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() {}

输出:

$ 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
$