Giter Club home page Giter Club logo

pcopy's Introduction

作用

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

pcopy's People

Contributors

dependabot[bot] avatar guonaihong avatar wangxin008 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

pcopy's Issues

slice in struct deepcopy failed

import (
	"testing"

	"github.com/antlabs/deepcopy"
)

func TestPointerCopy(t *testing.T) {
	type A struct {
		p []int
	}

	src := A{[]int{1}}
	t.Logf("%v", src)
	var dst A
	deepcopy.Copy(&dst, &src).Do()
	t.Logf("%v", dst)
	dst.p[0] = 2 // panic here
	if src.p[0] == dst.p[0] {
		t.Fail()
	}
}

and result:

=== RUN   TestPointerCopy
--- FAIL: TestPointerCopy (0.00s)
    deepcopy_test.go:15: {[1]}
    deepcopy_test.go:18: {[]}
panic: runtime error: index out of range [0] with length 0 [recovered]
        panic: runtime error: index out of range [0] with length 0

goroutine 7 [running]:
testing.tRunner.func1(0xc0000b2200)
        /usr/lib/go-1.13/src/testing/testing.go:874 +0x3a3
panic(0x5cfc00, 0xc0000162a0)
        /usr/lib/go-1.13/src/runtime/panic.go:679 +0x1b2
golang_test.TestPointerCopy(0xc0000b2200)
        /home/phenix/github/phenix3443/test/golang/deepcopy_test.go:19 +0x2a6
testing.tRunner(0xc0000b2200, 0x5f3bd0)
        /usr/lib/go-1.13/src/testing/testing.go:909 +0xc9
created by testing.(*T).Run
        /usr/lib/go-1.13/src/testing/testing.go:960 +0x350
FAIL    golang_test     0.010s
FAIL

性能压测

和src数据序列化成json。json反序列化到struct或者map,哪个快?

Changlog

草稿

0.普通递归拷贝

deepcopy.Copy(dst, src).Do()

1.控制拷贝层次,下面结构体只拷贝一层

deepcopy.Copy().MaxDepth(1).Do()

2.只拷贝指定tag的字段

deepcopy.Copy().RegisterTagName("copy").Do()

3.只拷贝string和int类型

deepcopy.Copy().OnlyType(reflect.String, reflect.Int).Do()

4.支持字段选择

deepcopy.Copy().OnlyField("id", "idx").Do()
deepcopy.Copy().OnlyField(".struct2", ".struct3").Do()

5.支持效验器,不符合要求的数据不copy

deepcopy.Copy().NeeValid(deepcopy.String()).Do()

TODO

第3,4,5条暂时不实现。

性能优化版本计划

6.15或者7月初
开始下个版本的性能优化,先尝试缓存解码器的思路。
使用指针优化

Feature Request: enable preheat by default

Thanks for your contribution, and I do have a suggestion which may make this library better.

I'm using this library to write a code generator with generics, and I cannot predict which specific type the user may call.

type DeepCopyGen[T any] interface {
	DeepCopyInto(*T)
}

func DeepCopy[T any](in, out *T) {
	var _in any = in
	if d, ok := _in.(DeepCopyGen[T]); ok {
		// use deepcopy-gen
		d.DeepCopyInto(out)
		return
	}
	// Have to call pcopy.Preheat each time
	pcopy.Copy(in, out, pcopy.WithUsePreheat())
}

if pcopy.Copy enables preheat by default, then things go easy. Just call pcopy.Copy(in, out) is enough.

The process could be as follows:

  1. split getFromCacheSetAndRun into get and run, the get function returns (*allFieldFunc, bool)
  2. if get returns nil, false, call newAllFieldFunc and saveToCache
  3. call *allFieldFunc.do
  4. the opts ...Option argument can be removed

What do you think? @guonaihong

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.