Deep Copy Object with Reflecting or gob in Go

In some case, we need to deep copy value of the object into another object. I have tested using the gob and reflecting to do that. The gob package gob manages streams of gobs - binary values exchanged between an Encoder (transmitter) and a Decoder (receiver).

Here is the benchmark file reflecting (github.com/mohae/deepcopy) vs 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)
}

Benchbark and profiling CPU and memory usage:

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

Deep Copy Object with Reflecting or gob in Go

Memory flame graph

Deep Copy Object with Reflecting or gob in Go

Deep copy with reflecting is 10x faster than gob and it will save more memory.

Deep Copy Object with Reflecting or gob in Go
5 votes, 4.20 avg. rating (85% score)