Go语言:几种深度拷贝(deepcopy)方法的性能对比

Go语言中所有赋值操作都是值传递,如果结构中不含指针,则直接赋值就是深度拷贝;如果结构中含有指针(包括自定义指针,以及切片,map等使用了指针的内置类型),则数据源和拷贝之间对应指针会共同指向同一块内存,这时深度拷贝需要特别处理。目前,有三种方法,一是用gob序列化成字节序列再反序列化生成克隆对象;二是先转换成json字节序列,再解析字节序列生成克隆对象;三是针对具体情况,定制化拷贝。前两种方法虽然比较通用但是因为使用了reflex反射,性能比定制化拷贝要低出2个数量级,所以在性能要求较高的情况下应该尽量避免使用前两者。

结论数据:

gob time:454μs
json time:170μs
custom time:2μs

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package main

import (
    "bytes"
    "encoding/gob"
    "encoding/json"
    "fmt"
    "time"
)

type AuthorInfo struct {
    Name    string `json:name`
    Age     int    `json:age`
    Country *int   `json:country`
}

type Book struct {
    Title    string            `json:title`
    Author   AuthorInfo        `json:author`
    Year     int               `json:year`
    Category []string          `json:category`
    Price    map[string]string `json:price`
}

func DeepCopyByGob(dst, src interface{}) error {
    var buffer bytes.Buffer
    if err := gob.NewEncoder(&buffer).Encode(src); err != nil {
        return err
    }

    return gob.NewDecoder(&buffer).Decode(dst)
}

func DeepCopyByJson(src []Book) (*[]Book, error) {
    var dst = new([]Book)
    b, err := json.Marshal(src)
    if err != nil {
        return nil, err
    }

    err = json.Unmarshal(b, dst)
    return dst, err
}

func DeepCopyByCustom(src []Book) []Book {
    dst := make([]Book, len(src))
    for i, book := range src {
        tmpbook := Book{}
        tmpbook.Title = book.Title
        tmpbook.Year = book.Year
        tmpbook.Author = AuthorInfo{}
        tmpbook.Author.Name = book.Author.Name
        tmpbook.Author.Age = book.Author.Age
        tmpbook.Author.Country = new(int)
        *tmpbook.Author.Country = *book.Author.Country
        tmpbook.Category = make([]string, len(book.Category))
        for index, category := range book.Category {
            tmpbook.Category[index] = category
        }
        tmpbook.Price = make(map[string]string)
        for k, v := range book.Price {
            tmpbook.Price[k] = v
        }
        dst[i] = tmpbook
    }
    return dst
}

func check(err error){
    if err != nil{
        panic(err)
    }
}

func print(name string, books []Book){
    for index,book := range books{
        fmt.Printf("%s[%d]=%v country=%d\n", name, index, book, *book.Author.Country)
    }
}

func main() {
    //初始化源Book切片
    books := make([]Book, 1)
    country := 1156
    author := AuthorInfo{"David", 38, &country}
    price := make(map[string]string)
    price["Europe"] = "$56"
    books[0] = Book{"Tutorial", author, 2020, []string{"math", "art"}, price}
    print("books",books)

    var err error
    var start time.Time

    //Gob拷贝
    start = time.Now()
    booksCpy := make([]Book, 1)
    err = DeepCopyByGob(&booksCpy, books)
    fmt.Printf("\ngob time:%v\n", time.Now().Sub(start))
    check(err)
    *booksCpy[0].Author.Country = 1134
    booksCpy[0].Category[0] = "literature"
    booksCpy[0].Price["America"] = "$250"
    print("booksCpy",booksCpy)
    print("books",books)

    //JSON拷贝
    start = time.Now()
    booksCpy2, err_json := DeepCopyByJson(books)
    fmt.Printf("\njson time:%v\n", time.Now().Sub(start))
    check(err_json)
    *(*booksCpy2)[0].Author.Country = 1135
    (*booksCpy2)[0].Category[0] = "science"
    (*booksCpy2)[0].Price["Canada"] = "$150"
    print("(*booksCpy2)",*booksCpy2)
    print("books",books)

    //定制拷贝
    start = time.Now()
    booksCpy3 := DeepCopyByCustom(books)
    fmt.Printf("\ncustom time:%v\n", time.Now().Sub(start))
    *booksCpy3[0].Author.Country = 1136
    booksCpy3[0].Category[0] = "geometry"
    booksCpy3[0].Price["Africa"] = "$34"
    print("booksCpy3",booksCpy3)
    print("books",books)
}

运行输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
books[0]={Tutorial {David 38 0xc000016178} 2020 [math art] map[Europe:$56]} country=1156

gob time:454.117μs
booksCpy[0]={Tutorial {David 38 0xc0000165d8} 2020 [literature art] map[America:$250 Europe:$56]} country=1134
books[0]={Tutorial {David 38 0xc000016178} 2020 [math art] map[Europe:$56]} country=1156

json time:170.338μs
(*booksCpy2)[0]={Tutorial {David 38 0xc000016878} 2020 [science art] map[Canada:$150 Europe:$56]} country=1135
books[0]={Tutorial {David 38 0xc000016178} 2020 [math art] map[Europe:$56]} country=1156

custom time:2.165μs
booksCpy3[0]={Tutorial {David 38 0xc0000168c8} 2020 [geometry art] map[Africa:$34 Europe:$56]} country=1136
books[0]={Tutorial {David 38 0xc000016178} 2020 [math art] map[Europe:$56]} country=1156