当前位置: 首页 > 面试题库 >

深度复制Golang中对象的更快方法

蒋星雨
2023-03-14
问题内容

我正在使用go 1.9。我想将对象的值复制到另一个对象中。我尝试用encoding / gob和encoding /
json做到这一点。但是gob编码比json编码花费更多的时间。我看像其他一些问题,这和他们建议采空区编码应该是更快的。但我看到完全相反的行为。有人可以告诉我我做错了什么吗?还是有比这两个更好,更快的方法来进行深度复制?我的对象的结构是复杂且嵌套的。

测试代码:

package main

import (
    "bytes"
    "encoding/gob"
    "encoding/json"
    "log"
    "time"

    "strconv"
)

// Test ...
type Test struct {
    Prop1 int
    Prop2 string
}

// Clone deep-copies a to b
func Clone(a, b interface{}) {

    buff := new(bytes.Buffer)
    enc := gob.NewEncoder(buff)
    dec := gob.NewDecoder(buff)
    enc.Encode(a)
    dec.Decode(b)
}

// DeepCopy deepcopies a to b using json marshaling
func DeepCopy(a, b interface{}) {
    byt, _ := json.Marshal(a)
    json.Unmarshal(byt, b)
}

func main() {
    i := 0
    tClone := time.Duration(0)
    tCopy := time.Duration(0)
    end := 3000
    for {
        if i == end {
            break
        }

        r := Test{Prop1: i, Prop2: strconv.Itoa(i)}
        var rNew Test
        t0 := time.Now()
        Clone(r, &rNew)
        t2 := time.Now().Sub(t0)
        tClone += t2

        r2 := Test{Prop1: i, Prop2: strconv.Itoa(i)}
        var rNew2 Test
        t0 = time.Now()
        DeepCopy(&r2, &rNew2)
        t2 = time.Now().Sub(t0)
        tCopy += t2

        i++
    }
    log.Printf("Total items %+v, Clone avg. %+v, DeepCopy avg. %+v, Total Difference %+v\n", i, tClone/3000, tCopy/3000, (tClone - tCopy))
}

我得到以下输出:

Total items 3000, Clone avg. 30.883µs, DeepCopy avg. 6.747µs, Total Difference 72.409084ms

问题答案:

JSON与html" target="_blank">gob差异

encoding/gob包需要发射类型定义:

该实现为流中的每种数据类型编译一个自定义编解码器,当使用单个Encoder传输值流时,效率最高,这将分摊编译成本。

当您“首先”序列化类型的值时,还必须包括/传输类型的 定义 ,因此解码器可以正确地解释和解码流:

一滴滴水是自我描述的。流中的每个数据项之前都有其类型的规范,以一小套预定义类型表示。

因此,在您的情况下,有必要每次都创建一个新的gob编码器和解码器,但这仍然是“瓶颈”,这是使其变慢的部分。从JSON格式编码/从JSON解码,表示中不包含类型描述。

为了证明这一点,请进行以下简单更改:

type Test struct {
    Prop1 [1000]int
    Prop2 [1000]string
}

我们在这里所做的是使字段数组的类型变为“值”千倍,而类型信息实际上保持不变(数组中的所有元素都具有相同的类型)。像这样创建它们的值:

r := Test{Prop1: [1000]int{}, Prop2: [1000]string{}}

现在运行您的测试程序,我机器上的输出:

原版的:

2017/10/17 14:55:53总项目3000,克隆平均。 33.63µs ,DeepCopy平均。 2.326µs
,总差93.910918ms

修改版本:

2017/10/17 14:56:38总项目3000,克隆平均。 119.899µs ,DeepCopy平均 462.608µs
,总差-1.02812648s

如您所见,在原始版本中,JSON更快,但在修改版本中,JSON gob变得更快,因为传输类型信息的成本摊销了。

测试/基准测试方法

现在介绍您的测试方法。这种测量性能的方法很差,并且可能会产生非常不准确的结果。相反,您应该使用Go的内置测试和基准测试工具。

这些克隆的警告

这些方法与反射一起使用,因此只能“克隆”可通过反射访问的字段,即:导出。同样,它们通常不管理指针相等。我的意思是,如果在结构中有2个指针字段,它们都指向同一个对象(指针相等),则在编组和解编组之后,您将获得2个指向2个不同值的不同指针。在某些情况下,这甚至可能导致问题。

正确的克隆方式

考虑到上述注意事项,通常正确的克隆方法需要“内部”的帮助。也就是说,通常只有在特定类型(或该类型的包)提供此功能的情况下,才可能克隆该特定类型。

是的,提供“手动”克隆功能并不方便,但另一方面,它的性能将优于上述方法(甚至可能高出几个数量级),并且需要克隆过程所需的最少“工作”内存。



 类似资料:
  • 问题 你想复制一个对象,包含其所有子对象。 解决方案 clone = (obj) -> if not obj? or typeof obj isnt 'object' return obj if obj instanceof Date return new Date(obj.getTime()) if obj instanceof RegExp flags

  • 问题内容: 据我了解,地图是Go中的参考类型。因此,分配将进行浅表复制。我计划在golang中进行Maps的递归深层复制。递归,因为我正在处理一个包含JSON的未编组内容的映射。 错误:无法使用(* dest)[key]的地址。(map [string] interface {})我该如何解决?还有其他方法可以绘制深层地图吗? 我在golang的map的内部入门,也将很有用。 问题答案:

  • 问题内容: 我想知道是否需要避免在创建一个带有嵌入式对象数组的简单对象时复制对对象的引用。情况如下:我有一个接受JSON并应用一些逻辑然后将对象存储在其中的服务器D B。可以说,我的表单用于保存DB中的团队。服务器接受team作为json。团队有一个TeamMember对象数组,我的表单有一个简单的字段来输入团队成员信息并将其添加到团队teamMembers数组中。现在这是一个问题,当我向数组列表

  • 问题内容: 现在并包含相同的日期- 从现在起三年。我想创建两个单独的日期时间,其中一个是从字符串中解析出来的,另一个是添加了三年的时间。目前,我已经将其修改为: 但这似乎是一个可怕的骇客。有没有“正确”的方法来深度复制DateTime对象? 问题答案: 更新: 如果要复制而不是引用现有的DT对象,请使用,而不是。

  • 编辑:@Rohit Jain 我的Recipe类和Ingreatent类(Recipe数组保存的对象)都有toString方法和recipes对Ingreats的调用,以便以一个漂亮的小格式将其全部打印出来。当我在我的“recipeone”对象(即意大利辣香肠披萨)上调用它时,我得到的是“意大利辣香肠披萨:1.0磅面团,8.0盎司酱汁,10.0盎司奶酪”。 然后我继续制作对象ressippi并将其

  • 问题内容: 我有一个叫Meal的课程 而且我有很多餐: 我想复制该数组,然后在其中一个实例中对Meal实例进行一些更改,而在第二个实例中不更改Meal实例,我将如何对其进行深拷贝? 问题答案: 由于是一个快速数组,所以该语句 将有效地复制原始数组。 但是,由于Meal是一 类 ,因此新数组将包含对原始膳食中相同膳食的引用。 如果您也想复制进餐内容,那么在一个数组中更改进餐不会在另一个数组中更改进餐