golang json 編碼 map 與struct 結構的對比

本文對比試驗採用官方包作json map 和struct 編碼。

原文鏈接:http://blog.lpflpf.cn/passage...linux

數據構造

map 數據類型爲map[string]string , key 長度爲10, val 長度爲100
struct 定義以下:git

type Object struct {
    Xvlbzgbaic string `json:"xvlbzgbaic"`
    Krbemfdzdc string `json:"krbemfdzdc"`
    Rzlntxyeuc string `json:"rzlntxyeuc"`
    Ctzkjkziva string `json:"ctzkjkziva"`
    Orsufumaps string `json:"orsufumaps"`
    Hyevwbtcml string `json:"hyevwbtcml"`
    Baatlyhdao string `json:"baatlyhdao"`
    Fkfohsvvxs string `json:"fkfohsvvxs"`
    Pqwarpxptp string `json:"pqwarpxptp"`
    Orvaukawww string `json:"orvaukawww"`
}

對比程序以下:github

obj := Object{}
    json.Unmarshal([]byte(str), &obj)

    start := time.Now()
    for i := 0; i < 1000000; i++ {
        json.Marshal(obj)
    }

    fmt.Println(time.Since(start))

    maps := map[string]string{}
    json.Unmarshal([]byte(str), &maps)

    start = time.Now()
    for i := 0; i < 1000000; i++ {
        json.Marshal(maps)
    }
    // 
    fmt.Println(time.Since(start))

其中,str 爲生成好的固定json數據, 咱們對相同的數據作json 編碼, 運行結果能夠看出,時間差距大約爲1倍,若將map的key 個數調整爲100個golang

運行次數均爲1000,000 次json

type\ keys 個數 10 100 1000
struct 3.84s 33.72s 5m42.34s
map[string]string 7.59s 1m20.03s 17m21.47s
no sorting map[string]string 6.40s 57.61s 10m4.39s
從上述對比中,得出以下結論:
在大量使用json 編碼時(尤爲是map結構較大時),請注意儘可能直接用struct,而不是用map作編碼。

緣由探究

  • map 編碼問題緩存

    • struct 屢次壓縮時,encoding 中會緩存 name 信息, 以及對應val的類型,直接調用相應的encoder 便可;相反,map 則每次須要對key 作反射,根據類型判斷獲取key的值,val值也須要反射獲取相應的encoder,時間浪費較多。
    • map 在作json 的解析的結果,會作排序操做。若修改源碼,將排序操做屏蔽,key 越多,須要的時間越多。
  • map 編碼
// go/src/encoding/json/encode.go 
func (me *mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
    if v.IsNil() {
        e.WriteString("null")
        return
    }
    e.WriteByte('{')

    // Extract and sort the keys.
    keys := v.MapKeys()
    sv := make([]reflectWithString, len(keys))
    for i, v := range keys {
        sv[i].v = v
        if err := sv[i].resolve(); err != nil {
            e.error(&MarshalerError{v.Type(), err})
        }
    }
    // 在輸出前會作key 的排序,最後按照key 排序的結果作輸出
    sort.Slice(sv, func(i, j int) bool { return sv[i].s < sv[j].s })

    for i, kv := range sv {
        if i > 0 {
            e.WriteByte(',')
        }
        e.string(kv.s, opts.escapeHTML)
        e.WriteByte(':')
        me.elemEnc(e, v.MapIndex(kv.v), opts)
    }
    e.WriteByte('}')
}
  • struct 編碼
// go/src/encoding/json/encode.go


type structEncoder struct {
    fields    []field
    fieldEncs []encoderFunc
}

func newStructEncoder(t reflect.Type) encoderFunc {
    fields := cachedTypeFields(t) // 從cache 中獲取fields
    se := &structEncoder{
        fields:    fields,
        fieldEncs: make([]encoderFunc, len(fields)),
    }
    for i, f := range fields {
        se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index))
    }
    return se.encode
}

func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
    e.WriteByte('{')
    first := true
    for i, f := range se.fields {   // fields 被緩存在structEncoder 結構體中
        fv := fieldByIndex(v, f.index)
        if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
            continue
        }
        if first {
            first = false
        } else {
            e.WriteByte(',')
        }
        e.string(f.name, opts.escapeHTML)
        e.WriteByte(':')
        opts.quoted = f.quoted
        se.fieldEncs[i](e, fv, opts)
    }
    e.WriteByte('}')
}

json-iterator/go

根據上述內容,對比github.com/json-iterator/go 與 encoding/json 的對比試驗,也能夠看出,iterator 對 map 的性能提高不是很明顯(因爲都須要作反射),後續將作試驗驗證。性能


Env

  • 機器環境: 1C1G
  • golang 版本: go1.10.3 linux/amd64
相關文章
相關標籤/搜索