2014-11-21 78 views
2

在Go中,深度複製切片的簡潔/良好表現方式是什麼?我需要將切片複製到新的後備數組中,因爲另一個數組擁有其他數據,並且可能在複製後進行修改。簡潔地複製一個切片?

目前,我正在做這樣的:

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

其中Torig元素類型。

+2

不能以一般的方式來完成。使用遞歸類型和遞歸數據結構和指針的語言對深拷貝很困難。什麼是一個指針,一個切片,一個指針切片,一個指向切片的指針的深層副本?現在撒上reflect.Value的... – Volker 2014-11-21 07:49:44

+0

也許我濫用術語深層複製,但我確實提到我想複製支持陣列的存儲。這就夠了。 – 2014-11-22 05:10:44

回答

0

看起來最快的方法是追加切片與必要的空間。我已經擴展了@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...) 
+0

我無法重現您的基準測試結果。請發佈您的基準代碼。 – peterSO 2014-11-30 01:01:16

+0

@peterSO:https://gist.github.com/anacrolix/e8baa9c70bbb2b909b19 – 2014-12-08 17:08:20

7

不確定哪溶液是最快而不基準,但一種替代方案是使用內置的copy

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

the documentation

FUNC拷貝(DST,SRC []類型) int

複製內置函數將源片段中的元素複製到 目標片段中。 (作爲特殊情況,它也會將 字符串中的字節複製到一段字節中。)源和目標可能會重疊。 複製返回複製的元素數量,這將是len(src)和len(dst)的最小值 。

注意

該解決方案將所有的值複製切片。如果切片包含帶指針字段的指針或結構,則這些指針值仍將指向與orig切片相同的值。

基準

標杆兩個選項,你可以看到他們有很相似的性能。

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

這是基準代碼:

包主要

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) 

,我猜想,這兩個調用執行填零。

+0

我感覺這將零cpy,然後複製它。我希望通過創建一個帶有必要上限的分片並將其添加到它來避免零分。 – 2014-11-22 05:12:14

+0

@MattJoiner:我做了一個基準,看看這兩個表現是否有所不同,但似乎不是。我用基準編輯了答案。 – ANisus 2014-11-22 10:47:52

+0

怎麼了所有的切片分配?做得好。我會自己運行這個基準。 – 2014-11-22 18:10:02

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  1ns/op 
BenchmarkAppendNil    200000  10022 ns/op 
ok  so/test 8.424s 
$