深度拷贝可以通过序列化和反序列化来实现,也可以基于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 与内存的开销也更优。