首頁 > 軟體

golang 物件深拷貝的常見方式及效能

2022-06-20 14:02:50

關於golang拷貝的概念

Go語言中所有賦值操作都是值傳遞,如果結構中不含指標,則直接賦值就是深度拷貝;如果結構中含有指標(包括自定義指標,以及切片,map等使用了指標的內建型別),則資料來源和拷貝之間對應指標會共同指向同一塊記憶體,這時深度拷貝需要特別處理。目前,有三種方法,一是用gob序列化成位元組序列再反序列化生成克隆物件;二是先轉換成json位元組序列,再解析位元組序列生成克隆物件;三是針對具體情況,客製化化拷貝。前兩種方法雖然比較通用但是因為使用了reflex反射,效能比客製化化拷貝要低出2個數量級,所以在效能要求較高的情況下應該儘量避免使用前兩者。

完整程式碼

package json
import (
   "bytes"
   "encoding/gob"
   "encoding/json"
   "fmt"
   "testing"
)
/**
 * @Description: 請求資訊
 */
type BidRequest struct {
   ID     string  `json:"id"`
   Imp    []*Imp  `json:"imp"`
   Device *Device `json:"device"`
}
/**
 * @Description: imp物件
 */
type Imp struct {
   ID          string  `json:"id"`
   Tagid       string  `json:"tagid"`
   Bidfloor    float64 `json:"bidfloor"`
}
/**
 * @Description: 裝置資訊
 */
type Device struct {
   Ua         string `json:"ua"`
   IP         string `json:"ip"`
   Geo        *Geo   `json:"geo"`
   Make       string `json:"make"`
   Model      string `json:"model"`
   Os         string `json:"os"`
   Osv        string `json:"osv"`
}
/**
 * @Description: 地理位置資訊
 */
type Geo struct {
   Lat     int    `json:"lat"`
   Lon     int    `json:"lon"`
   Country string `json:"country"`
   Region  string `json:"region"`
   City    string `json:"city"`
}
/**
 * @Description: 利用gob進行深拷貝
 */
func DeepCopyByGob(src,dst interface{}) error {
   var buffer bytes.Buffer
   if err := gob.NewEncoder(&buffer).Encode(src); err != nil {
      return err
   }
   return gob.NewDecoder(&buffer).Decode(dst)
}
/**
 * @Description: 利用json進行深拷貝
 */
func DeepCopyByJson(src,dst *BidRequest) error{
   if tmp, err := json.Marshal(&src);err!=nil{
      return err
   }else {
      err = json.Unmarshal(tmp, dst)
      return err
   }
}

/**
 * @Description: 通過自定義進行copy
 */
func DeepCopyByCustom(src,dst *BidRequest){
   dst.ID=src.ID
   dst.Device=&Device{
      Ua: src.Device.Ua,
      IP: src.Device.IP,
      Geo: &Geo{
         Lat: src.Device.Geo.Lat,
         Lon: src.Device.Geo.Lon,
      },
      Make: src.Device.Make,
      Model: src.Device.Model,
      Os: src.Device.Os,
      Osv: src.Device.Osv,
   }
   dst.Imp=make([]*Imp,len(src.Imp))
   for index,imp:=range src.Imp{
      //注意此處因為imp物件裡無指標物件,所以可以直接使用等於
      dst.Imp[index]=imp
   }
}

func initData()*BidRequest  {
   str:="{"id":"MM7dIXz4H05qtmViqnY5dW","imp":[{"id":"1","tagid":"3979722720","bidfloor":0.01}],"device":{"ua":"Mozilla/5.0 (Linux; Android 10; SM-G960N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.115 Mobile Safari/537.36 (Mobile; afma-sdk-a-v212621039.212621039.0)","ip":"192.168.1.0","geo":{"lat":0,"lon":0,"country":"KOR","region":"KR-11","city":"Seoul"},"make":"samsung","model":"sm-g960n","os":"android","osv":"10"}}"
   ans:=new(BidRequest)
   json.Unmarshal([]byte(str),&ans)
   return ans
}

/**
 * @Description: 壓測深拷貝 -gob
 */
func BenchmarkDeepCopy_Gob(b *testing.B)  {
   src:=initData()
   b.ResetTimer()
   for i:=0;i<b.N;i++{
      DeepCopyByGob(src,new(BidRequest))
   }
}

/**
 * @Description: 壓測深拷貝 -json
 */
func BenchmarkDeepCopy_Json(b *testing.B)  {
   src:=initData()
   b.ResetTimer()
   for i:=0;i<b.N;i++{
      DeepCopyByJson(src,new(BidRequest))
   }
}
/**
 * @Description: 壓測深拷貝 -custom
 */
func BenchmarkDeepCopy_custom(b *testing.B)  {
   src:=initData()
   b.ResetTimer()
   for i:=0;i<b.N;i++{
      DeepCopyByCustom(src,new(BidRequest))
   }
}
/**
 * @Description: 測試拷貝是否ok
 */
func TestCpoyIsOk(t *testing.T)  {
   src:=initData()
   //1.gob
   dst01:=new(BidRequest)
   DeepCopyByGob(src,dst01)
   bs01, _ := json.Marshal(dst01)
   fmt.Printf("%vn",string(bs01))
   //2.json
   dst02:=new(BidRequest)
   DeepCopyByJson(src,dst02)
   bs02, _ := json.Marshal(dst02)
   fmt.Printf("%vn",string(bs02))
   //3.custom
   dst03:=new(BidRequest)
   DeepCopyByCustom(src,dst03)
   bs03, _ := json.Marshal(dst02)
   fmt.Printf("%vn",string(bs03))
}

先執行 TestCpoyIsOk,驗證三種方式的輸出是否ok,其驗證結果如下:

benmark三種copy方式:

執行命令 go test -bench=. -benchmem 可以同時檢視到每次操作的記憶體和耗時的情況

總結

可以看到 從效能上來講 custom>json>gob,從程式碼數量上來講 gob>json>custom ,因此具體使用時應該充分考慮效能和程式碼複雜度,若效能要求不是很高建議gob方法,其比較簡潔並且利於生成工具包,若要求效能則儘量使用custom,此處不偷懶可以提高效能哦。若是效能要求在中間,則可以使用json先序列化,再反序列化賦值。

到此這篇關於golang 物件深拷貝的常見方式及效能的文章就介紹到這了,更多相關golang 深拷貝內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com