V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
guonaihong
V2EX  ›  Go 编程语言

深度拷贝 pcopy,性能升级版本来了

  •  1
     
  •   guonaihong ·
    guonaihong · 2023-05-18 13:44:18 +08:00 · 2768 次点击
    这是一个创建于 584 天前的主题,其中的信息可能已经有所发展或是发生改变。

    地址

    https://github.com/antlabs/pcopy

    作用

    Go codecov

    pcopy.Copy主要用于两个类型间的深度拷贝, 前身是 deepcopy

    新加预热函数。Copy 时打开加速开关,达到性能提升 4-10 倍的效果。

    警告:

    高性能的同时可能会有些 bug, 如果发现 bug 可以去掉pcopy.WithUsePreheat()试下, 结果不一致,可以提 issue 。

    feature

    • 高性能, 相对第一个版本提升 4-10 倍的性能
    • 支持异构结构体拷贝, dst 和 src 可以是不同的类型,会拷贝 dst 和 src 交集的部分
    • 多类型支持 struct/map/slice/array/int...int64/uint...uint64/ 等等

    内容

    Installation

    go get github.com/antlabs/pcopy
    

    Quick start

    package main
    
    import (
        "fmt"
        "github.com/antlabs/pcopy"
    )
    
    type dst struct {
        ID int
        Result string
    }
    
    type src struct{
        ID int
        Text string
    }
    func main() {
       d, s := dst{}, src{ID:3}
       pcopy.Preheat(&dst{}, &src{}) // 一对类型只要预热一次
       pcopy.Copy(&d, &s, pcopy.WithUsePreheat())
       fmt.Printf("%#v\n", d)
    
    }
    
    

    copy slice

    package main
    
    import (
            "fmt"
    
            "github.com/antlabs/pcopy"
    )
    
    func main() {
            i := []int{1, 2, 3, 4, 5, 6}
            var o []int
    
            pcopy.Preheat(&o, &i)
            pcopy.Copy(&o, &i, pcopy.WithUsePreheat())
    
            fmt.Printf("%#v\n", o)
    }
    
    

    copy map

    package main
    
    import (
            "fmt"
    
            "github.com/antlabs/pcopy"
    )
    
    func main() {
            i := map[string]int{
                    "cat":  100,
                    "head": 10,
                    "tr":   3,
                    "tail": 44,
            }
    
            var o map[string]int
            pcopy.Preheat(&o, &i)
            pcopy.Copy(&o, &i, pcopy.WithUsePreheat())
    
            fmt.Printf("%#v\n", o)
    }
    
    

    simplify business code development

    经常看到,对同一个结构体的,有值更新操作,都是一堆手工 if 然后赋值的代码。不仅容易出错,还累。快使用 pcopy 解放双手。

    type option struct {
            Int int
            Float64 float64
            S  string
    }
    
    func main() {
            var a, b option
            if b.Int != 0 {
                    a.Int = b.Int
            }
    
            if b.Float64 != 0.0 {
                    a.Float64 = b.Float64
            }
    
            if b.S != "" {
                    a.S = b.S
            }
    
            pcopy.Preheat(&a, &b) //只要预热一次
            //可以约化成
            pcopy.Copy(&a, &b, pcopy.WithUsePreheat())
    }
    

    benchmark

    从零实现的 pcopy 相比 json 序列化与反序列化方式拥有更好的性能

    压测仓库位置

    goos: darwin
    goarch: arm64
    pkg: benchmark
    Benchmark_Use_reflectValue_MiniCopy-8   	  334728	      3575 ns/op
    Benchmark_Use_reflectValue_DeepCopy-8   	  595302	      1956 ns/op
    Benchmark_Use_reflectValue_Copier-8     	  203574	      5860 ns/op
    Benchmark_Use_Ptr_jsoniter-8            	  821113	      1477 ns/op
    Benchmark_Use_Ptr_pcopy-8               	 3390382	       354.0 ns/op
    Benchmark_Use_Ptr_coven-8               	 1414197	       848.7 ns/op
    PASS
    ok  	benchmark	9.771s
    

    本项目压测

    从下面的压测数据可以看到,基本提供了 4-10 倍的性能提升

    goos: darwin
    goarch: arm64
    pkg: github.com/antlabs/pcopy
    Benchmark_BaseMap_Unsafe_Pcopy-8               	  529747	      2343 ns/op
    Benchmark_BaseMap_miniCopy-8                   	   62181	     19212 ns/op
    Benchmark_BaseMap_Reflect-8                    	   93810	     12756 ns/op
    Benchmark_BaseSlice_Unsafe_Pcopy-8             	 2013764	       595.1 ns/op
    Benchmark_BaseSlice_miniCopy-8                 	  154918	      7728 ns/op
    Benchmark_BaseSlice_Reflect-8                  	  188720	      6393 ns/op
    Benchmark_BaseType_Unsafe_Pcopy-8              	 4872112	       243.8 ns/op
    Benchmark_BaseType_MiniCopy-8                  	  517814	      2278 ns/op
    Benchmark_BaseType_Pcopy-8                     	  635156	      1886 ns/op
    Benchmark_CompositeMap_Unsafe_Pcopy-8          	  486253	      2409 ns/op
    Benchmark_CompositeMap_miniCopy-8              	  229674	      5173 ns/op
    Benchmark_CompositeMap_Reflect-8               	  475243	      2490 ns/op
    Benchmark_GetLikeFavorited_Unsafe_Pcopy2-8     	  446907	      2662 ns/op
    Benchmark_GetLikeFavorited_Unsafe_Pcopy-8      	  470217	      2572 ns/op
    Benchmark_GetLikeFavorited_MiniCopy-8          	   85674	     13989 ns/op
    Benchmark_GetLikeFavorited_Reflect_Pcopy-8     	  121603	      9856 ns/op
    Benchmark_GetRedPoint_Unsafe_Pcopy-8           	 1626688	       736.1 ns/op
    Benchmark_GetRedPoint_MiniCopy-8               	  650004	      1871 ns/op
    Benchmark_GetRedPoint_Reflect_Pcopy-8          	 1669778	       722.0 ns/op
    Benchmark_Interface_Unsafe_Pcopy-8             	 2869022	       421.3 ns/op
    Benchmark_Interface_MiniCopy-8                 	  413936	      2704 ns/op
    Benchmark_Interface_Pcopy-8                    	  440250	      2688 ns/op
    Benchmark_Interface_BaseSlice_Unsafe_Pcopy-8   	 1266501	       947.4 ns/op
    Benchmark_Interface_BaseSlice_MiniCopy-8       	  141610	      8422 ns/op
    Benchmark_Interface_BaseSlice_Pcopy-8          	  203906	      5917 ns/op
    Benchmark_Ptr_BaseType1_Unsafe_Pcopy-8         	  910153	      1310 ns/op
    Benchmark_Ptr_BaseType1_Reflect_Pcopy-8        	  391117	      3026 ns/op
    Benchmark_Ptr_BaseSlice_Unsafe_Pcopy-8         	  698156	      1704 ns/op
    Benchmark_Ptr_BaseSlice_Reflect_Pcopy-8        	  219999	      5415 ns/op
    Benchmark_SliceWithStruct_Unsafe_Pcopy-8       	 1395982	       860.3 ns/op
    Benchmark_SliceWithStruct_miniCopy-8           	  163154	      7298 ns/op
    Benchmark_SliceWithStruct_Reflect_Pcopy-8      	  190728	      6213 ns/op
    
    13 条回复    2023-07-19 17:42:06 +08:00
    matrix1010
        1
    matrix1010  
       2023-05-18 14:26:50 +08:00
    你这 codecov 42%有点难看啊, 我觉得低于 70%就别放了
    SingeeKing
        2
    SingeeKing  
       2023-05-18 15:46:45 +08:00
    感觉基于反射实现的一个最大的问题就是不支持私有字段,十分难受
    guonaihong
        3
    guonaihong  
    OP
       2023-05-18 15:53:10 +08:00
    @matrix1010 后面再提高下,影响测试覆盖度的都是用 tmpl 生成的代码。
    shockerli
        4
    shockerli  
       2023-05-18 18:08:36 +08:00
    一直在用 copier
    phithon
        5
    phithon  
       2023-05-18 18:41:27 +08:00
    同用 copier
    bthulu
        6
    bthulu  
       2023-05-18 19:14:39 +08:00
    能支持多语言吗?
    guonaihong
        7
    guonaihong  
    OP
       2023-05-18 21:09:09 +08:00
    @shockerli @phithon copier 从压测数据来看不太快。pcony 运行时间是 copier 的 1/16.
    guonaihong
        8
    guonaihong  
    OP
       2023-05-18 21:09:25 +08:00
    @bthulu 怎么理解?
    ToBeHacker
        9
    ToBeHacker  
       2023-05-19 03:15:52 +08:00
    有必要这么搞么,使用大量的反射来进行 deep copy 还是太重了吧
    xuanbg
        10
    xuanbg  
       2023-05-19 08:11:05 +08:00
    有和我一样直接序列化再反序列化完事的么……
    bv
        11
    bv  
       2023-05-19 09:09:39 +08:00
    @xuanbg 那估计少有,哈哈
    guonaihong
        12
    guonaihong  
    OP
       2023-05-21 21:59:51 +08:00
    @ToBeHacker 这么做只是为了可控性。能不停进行性能的提升。假如跳开这个库的话题,回到大家熟悉的服务端,我们可以用标准库快速一个 server 。如果要进行性能的提升,可能要进行一大量的定制开发(库作者的角度),比如 epoll+协程池+gc 优化。它背后的思路一样,开箱即用的东西,性能不好提升。
    guonaihong
        13
    guonaihong  
    OP
       2023-07-19 17:42:06 +08:00
    @SingeeKing 要支持的话也很简单,拿到这个字段的指针地址,后面直接修改就行。但是 pcopy 不会支持修改私有字段的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2436 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 15:48 · PVG 23:48 · LAX 07:48 · JFK 10:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.