• 【golang】深拷贝-浅拷贝-方式与性能对比



    在这里插入图片描述

    概念

    各编程语言都有深浅拷贝的概念, 它们的核心是拷贝的是地址(浅拷贝)还是(深拷贝), 今天我们就对其概念介绍, 并做性能对比和特性对比

    浅拷贝

    • 拷贝的只是数据的地址. 新老对象共享内存
    • 当改新对象时, 老对象也会被改变. 当释放新对象时, 老对象也会被释放.
    • 浅拷贝的数据类型, 即引用类型数据: 有Slice(无论原生类型或结构体slice), Map

    深拷贝

    • 深拷贝的是数据的内容, 会创建新对象, 新老对象不共享内存
    • 当改变新对象时, 和老对象没关系; 当释放新对象时, 和老对象也没关系; 总之就是新老对象没任何关系
    • 深拷贝的数据类型, 即值类型数据: 有Struct, Array, Int, String, Float, Bool

    用途

    当我们需要拷贝一个新对象时, 怎么选择呢? 显然

    • 当需要节省内存时, 可选浅拷贝, 因为只是复制了地址
    • 当为了操作简便时, 可选浅拷贝, 直接a := b即可
    • 而当需要开辟新内存, 分别处理新老对象, 使两者互不干扰时, 则需要深拷贝
      • 例如数据流业务的多条支流间, 若均存在改写对象的操作, 则需深拷贝, 防止A逻辑分支改了a对象, 而后续B逻辑分支意外的使用了被别人改过的对象

    在这里插入图片描述

    性能对比

    有Reflect, GobEncode, JsonMarshal几种方式, 我们做一个Benchmark试验如下

    • 定义数据类型
    type InnerStruct struct {
    	Int    int
    	String string
    }
    type CType struct {
    	Int         int
    	String      string
    	Struct      InnerStruct
    	IntSlice    []int
    	StringSlice []string
    	StructSlice []InnerStruct
    	IntArray    [2]int
    	StringArray [2]string
    	StructArray [2]InnerStruct
    	Map         map[string]string
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 初始化数据
    c := &CType{
    	Int:         1,
    	String:      "2",
    	Struct:      InnerStruct{Int: 3, String: "4"},
    	IntSlice:    []int{5, 6},
    	StringSlice: []string{"7", "8"},
    	StructSlice: []InnerStruct{{Int: 9, String: "10"}, {Int: 11, String: "12"}},
    	IntArray:    [2]int{13, 14},
    	StringArray: [2]string{"15", "16"},
    	StructArray: [2]InnerStruct{{Int: 17, String: "18"}, {Int: 19, String: "20"}},
    	Map:         map[string]string{"21": "22", "23": "24"},
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    测试电脑为cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz

    浅拷贝

    func BenchmarkShallowCopy(b *testing.B) {
    	for n := 0; n < b.N; n++ {
    		c.ShallowCopy()
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果为BenchmarkShallowCopy 0.7702 ns/op

    Reflect

    func BenchmarkReflect(b *testing.B) {
    	for n := 0; n < b.N; n++ {
    		c.DeepCopyReflect()
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果为BenchmarkReflect 4515 ns/op

    JsonMarshal

    func BenchmarkJsonMarshal(b *testing.B) {
    	for n := 0; n < b.N; n++ {
    		c.DeepCopyJsonMarshal()
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果为BenchmarkJsonMarshal 11133 ns/op

    GobEncode

    func BenchmarkGobMarshal(b *testing.B) {
    	for n := 0; n < b.N; n++ {
    		c.DeepCopyGobMarshal()
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果为BenchmarkGobMarshal 54298 ns/op
    综上, 浅拷贝性能>Reflect性能>JsonMarshal性能>GobMarshal

    在这里插入图片描述

    特点对比

    做完性能对别后, 我们还需对别深浅拷贝是否对新对象的写操作有影响

    同样用上文性能对比的数据, 分别测试对拷贝后新对象的写操作, 是否影响老对象, 结果如下

    浅拷贝

    func TestShallowCopy(t *testing.T) {
    	r := c.ShallowCopy()
    	r.Int += 10000
    	r.String += "changed"
    	r.Struct.Int += 10000
    	r.Struct.String += "changed"
    	r.IntSlice[0] += 10000
    	r.IntSlice[1] += 10000
    	r.StringSlice[0] += "changed"
    	r.StringSlice[1] += "changed"
    	r.StructSlice[0].Int += 10000
    	r.StructSlice[0].String += "changed"
    	r.StructSlice[1].Int += 10000
    	r.StructSlice[1].String += "changed"
    	r.IntArray[0] += 10000
    	r.IntArray[1] += 10000
    	r.StringArray[0] += "changed"
    	r.StringArray[1] += "changed"
    	r.StructArray[0].Int += 10000
    	r.StructArray[0].String += "changed"
    	r.StructArray[1].Int += 10000
    	r.StructArray[1].String += "changed"
    	r.Map["21"] += "changed"
    	r.Map["23"] += "changed"
    	rByte, _ := json.MarshalIndent(r, "", "  ")
    	t.Log(string(rByte))
    }
    
    • 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

    结果如下可见, 确实只有Slice(无论原生类型或结构体slice), Map有影响, 其他类型均无影响

    === RUN   TestShallowCopy
        main_test.go:117: {
              "Int": 1,
              "String": "2",
              "Struct": {
                "Int": 3,
                "String": "4"
              },
              "IntSlice": [
                10005,
                10006
              ],
              "StringSlice": [
                "7changed",
                "8changed"
              ],
              "StructSlice": [
                {
                  "Int": 10009,
                  "String": "10changed"
                },
                {
                  "Int": 10011,
                  "String": "12changed"
                }
              ],
              "IntArray": [
                13,
                14
              ],
              "StringArray": [
                "15",
                "16"
              ],
              "StructArray": [
                {
                  "Int": 17,
                  "String": "18"
                },
                {
                  "Int": 19,
                  "String": "20"
                }
              ],
              "Map": {
                "21": "22changed",
                "23": "24changed"
              }
            }
    
    • 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

    Reflect

    func TestDeepCopyReflect(t *testing.T) {
    	r := c.DeepCopyReflect()
    	r.Int += 10000
    	r.String += "changed"
    	r.Struct.Int += 10000
    	r.Struct.String += "changed"
    	r.IntSlice[0] += 10000
    	r.IntSlice[1] += 10000
    	r.StringSlice[0] += "changed"
    	r.StringSlice[1] += "changed"
    	r.StructSlice[0].Int += 10000
    	r.StructSlice[0].String += "changed"
    	r.StructSlice[1].Int += 10000
    	r.StructSlice[1].String += "changed"
    	r.IntArray[0] += 10000
    	r.IntArray[1] += 10000
    	r.StringArray[0] += "changed"
    	r.StringArray[1] += "changed"
    	r.StructArray[0].Int += 10000
    	r.StructArray[0].String += "changed"
    	r.StructArray[1].Int += 10000
    	r.StructArray[1].String += "changed"
    	r.Map["21"] += "changed"
    	r.Map["23"] += "changed"
    	rByte, _ := json.MarshalIndent(c, "", "  ")
    	t.Log(string(rByte))
    }
    
    • 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

    结果如下, 确实新对象的任何改动, 都没影响老对象

    === RUN   TestDeepCopyReflect
        main_test.go:117: {
              "Int": 1,
              "String": "2",
              "Struct": {
                "Int": 3,
                "String": "4"
              },
              "IntSlice": [
                5,
                6
              ],
              "StringSlice": [
                "7",
                "8"
              ],
              "StructSlice": [
                {
                  "Int": 9,
                  "String": "10"
                },
                {
                  "Int": 11,
                  "String": "12"
                }
              ],
              "IntArray": [
                13,
                14
              ],
              "StringArray": [
                "15",
                "16"
              ],
              "StructArray": [
                {
                  "Int": 17,
                  "String": "18"
                },
                {
                  "Int": 19,
                  "String": "20"
                }
              ],
              "Map": {
                "21": "22",
                "23": "24"
              }
            }
    
    • 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

    JsonMarshal

    func TestDeepCopyJsonMarshal(t *testing.T) {
    	r := c.DeepCopyJsonMarshal()
    	r.Int += 10000
    	r.String += "changed"
    	r.Struct.Int += 10000
    	r.Struct.String += "changed"
    	r.IntSlice[0] += 10000
    	r.IntSlice[1] += 10000
    	r.StringSlice[0] += "changed"
    	r.StringSlice[1] += "changed"
    	r.StructSlice[0].Int += 10000
    	r.StructSlice[0].String += "changed"
    	r.StructSlice[1].Int += 10000
    	r.StructSlice[1].String += "changed"
    	r.IntArray[0] += 10000
    	r.IntArray[1] += 10000
    	r.StringArray[0] += "changed"
    	r.StringArray[1] += "changed"
    	r.StructArray[0].Int += 10000
    	r.StructArray[0].String += "changed"
    	r.StructArray[1].Int += 10000
    	r.StructArray[1].String += "changed"
    	r.Map["21"] += "changed"
    	r.Map["23"] += "changed"
    	rByte, _ := json.MarshalIndent(c, "", "  ")
    	t.Log(string(rByte))
    }
    
    • 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

    结果如下, 确实新对象的任何改动, 都没影响老对象

    === RUN   TestDeepCopyJsonMarshal
        main_test.go:117: {
              "Int": 1,
              "String": "2",
              "Struct": {
                "Int": 3,
                "String": "4"
              },
              "IntSlice": [
                5,
                6
              ],
              "StringSlice": [
                "7",
                "8"
              ],
              "StructSlice": [
                {
                  "Int": 9,
                  "String": "10"
                },
                {
                  "Int": 11,
                  "String": "12"
                }
              ],
              "IntArray": [
                13,
                14
              ],
              "StringArray": [
                "15",
                "16"
              ],
              "StructArray": [
                {
                  "Int": 17,
                  "String": "18"
                },
                {
                  "Int": 19,
                  "String": "20"
                }
              ],
              "Map": {
                "21": "22",
                "23": "24"
              }
            }
    
    • 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

    GobEncode

    func TestDeepCopyGobMarshal(t *testing.T) {
    	r := c.DeepCopyGobMarshal()
    	r.Int += 10000
    	r.String += "changed"
    	r.Struct.Int += 10000
    	r.Struct.String += "changed"
    	r.IntSlice[0] += 10000
    	r.IntSlice[1] += 10000
    	r.StringSlice[0] += "changed"
    	r.StringSlice[1] += "changed"
    	r.StructSlice[0].Int += 10000
    	r.StructSlice[0].String += "changed"
    	r.StructSlice[1].Int += 10000
    	r.StructSlice[1].String += "changed"
    	r.IntArray[0] += 10000
    	r.IntArray[1] += 10000
    	r.StringArray[0] += "changed"
    	r.StringArray[1] += "changed"
    	r.StructArray[0].Int += 10000
    	r.StructArray[0].String += "changed"
    	r.StructArray[1].Int += 10000
    	r.StructArray[1].String += "changed"
    	r.Map["21"] += "changed"
    	r.Map["23"] += "changed"
    	rByte, _ := json.MarshalIndent(c, "", "  ")
    	t.Log(string(rByte))
    }
    
    • 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

    结果如下, 确实新对象的任何改动, 都没影响老对象

    === RUN   TestDeepCopyGobMarshal
        main_test.go:117: {
              "Int": 1,
              "String": "2",
              "Struct": {
                "Int": 3,
                "String": "4"
              },
              "IntSlice": [
                5,
                6
              ],
              "StringSlice": [
                "7",
                "8"
              ],
              "StructSlice": [
                {
                  "Int": 9,
                  "String": "10"
                },
                {
                  "Int": 11,
                  "String": "12"
                }
              ],
              "IntArray": [
                13,
                14
              ],
              "StringArray": [
                "15",
                "16"
              ],
              "StructArray": [
                {
                  "Int": 17,
                  "String": "18"
                },
                {
                  "Int": 19,
                  "String": "20"
                }
              ],
              "Map": {
                "21": "22",
                "23": "24"
              }
            }
    
    • 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
  • 相关阅读:
    ubuntu网络配置
    [软件安装] tmux安装及相关事项
    fwknop服务端代码理解
    什么是FSDM的九大数据概念
    中秋征文“好文尝鲜奖”榜单公布,快来领取你的定制礼盒!
    如何打造小红书产品差异化,打造产品优势?
    Mysql主从搭建
    Python与ArcGIS系列(九)自定义python地理处理工具
    A-Level化学半反应配平法
    vite vue3 pwa 更新提醒
  • 原文地址:https://blog.csdn.net/jiaoyangwm/article/details/126592385