深度拷貝可以通過序列化和反序列化來實現,也可以基於reflect包的反射機制完成。我對於這兩種方式實現深拷貝做了性能基準測試。

下面是對比反射(github.com/mohae/deepcopy)與序列化(gob)所用的基準測試腳本 deepcopy_test.go

package deepcopy

import (
"bytes"
"encoding/gob"
"testing"

"github.com/mohae/deepcopy"
)

type Basics struct {
String string
Strings []string
StringArr [4]string
Bool bool
Bools []bool
Byte byte
Bytes []byte
Int int
Ints []int
Int8 int8
Int8s []int8
Int16 int16
Int16s []int16
Int32 int32
Int32s []int32
Int64 int64
Int64s []int64
Uint uint
Uints []uint
Uint8 uint8
Uint8s []uint8
Uint16 uint16
Uint16s []uint16
Uint32 uint32
Uint32s []uint32
Uint64 uint64
Uint64s []uint64
Float32 float32
Float32s []float32
Float64 float64
Float64s []float64
Complex64 complex64
Complex64s []complex64
Complex128 complex128
Complex128s []complex128
Interface interface{}
Interfaces []interface{}
}

var src = Basics{
String: "kimchi",
Strings: []string{"uni", "ika"},
StringArr: [4]string{"malort", "barenjager", "fernet", "salmiakki"},
Bool: true,
Bools: []bool{true, false, true},
Byte: z,
Bytes: []byte("abc"),
Int: 42,
Ints: []int{0, 1, 3, 4},
Int8: 8,
Int8s: []int8{8, 9, 10},
Int16: 16,
Int16s: []int16{16, 17, 18, 19},
Int32: 32,
Int32s: []int32{32, 33},
Int64: 64,
Int64s: []int64{64},
Uint: 420,
Uints: []uint{11, 12, 13},
Uint8: 81,
Uint8s: []uint8{81, 82},
Uint16: 160,
Uint16s: []uint16{160, 161, 162, 163, 164},
Uint32: 320,
Uint32s: []uint32{320, 321},
Uint64: 640,
Uint64s: []uint64{6400, 6401, 6402, 6403},
Float32: 32.32,
Float32s: []float32{32.32, 33},
Float64: 64.1,
Float64s: []float64{64, 65, 66},
Complex64: complex64(-64 + 12i),
Complex64s: []complex64{complex64(-65 + 11i), complex64(66 + 10i)},
Complex128: complex128(-128 + 12i),
Complex128s: []complex128{complex128(-128 + 11i), complex128(129 + 10i)},
Interfaces: []interface{}{42, true, "pan-galactic"},
}

func Benchmark_GOBDeepCopy(b *testing.B) {
// use b.N for looping
for i := 0; i < b.N; i++ {
var dst Basics
err := GOBDeepCopy(&dst, &src)
if err != nil {
b.Error(err)
}
}
}

func Benchmark_ReflectDeepCopy(b *testing.B) {
// use b.N for looping
for i := 0; i < b.N; i++ {
dst := deepcopy.Copy(src).(Basics)
if !dst.Bool {
b.Error("reflect deep copy failed")
}
}
}

// GOBDeepCopy provides the method to creates a deep copy of whatever is passed to
// it and returns the copy in an interface. The returned value will need to be
// asserted to the correct type.
func GOBDeepCopy(dst, src interface{}) error {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(src); err != nil {
return err
}
return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
}

進行持續時長為 10s 的基準測試並對 CPU 和內存使用情況做記錄:

$ go test -v -run=^$ -bench=. -benchtime=10s -cpuprofile=prof.cpu -memprofile=prof.mem -memprofilerate=2
goos: darwin
goarch: amd64
Benchmark_GOBDeepCopy-4 5000 2918910 ns/op
Benchmark_ReflectDeepCopy-4 50000 289784 ns/op
PASS
ok _/Users/xuri/Desktop/deepcopy 32.421s

使用火焰圖分析 CPU 情況

深拷貝內存分析

通過對比可以看出,反射比 gob 序列化來實現深拷貝速度快 10 倍,CPU 與內存的開銷也更優。


推薦閱讀:
相关文章