探究|Go JSON 三方包哪家強?

来源:https://www.cnblogs.com/88223100/archive/2023/06/04/Which-is-the-strongest-third-party-package-for-Go-JSON.html
-Advertisement-
Play Games

本文作者從評判標準、功能評測、性能評測等多方面剖析三方庫哪些家強,並給出了比較務實的建議。 引言 為了小伙伴理解,彙總了一下文章中會提及的術語名詞解釋,請放心品讀,歡迎一起討論交流! 你真的瞭解 Go 標準庫嗎? 問題一:標準庫可以反序列化普通的字元串嗎?執行下麵的代碼會報錯嗎? var s str ...


本文作者從評判標準、功能評測、性能評測等多方面剖析三方庫哪些家強,並給出了比較務實的建議。

引言

為了小伙伴理解,彙總了一下文章中會提及的術語名詞解釋,請放心品讀,歡迎一起討論交流!

圖片

 

你真的瞭解 Go 標準庫嗎?

問題一:標準庫可以反序列化普通的字元串嗎?執行下麵的代碼會報錯嗎?


var s string
err := json.Unmarshal([]byte(`"Hello, world!"`), &s)
assert.NoError(t, err)
fmt.Println(s)
// 輸出:
// Hello, world!

解:其實標準庫解析不僅支持是對象、數組,同時也可以是字元串、數值、布爾值以及空值,但需要註意,上面字元串中的雙引號不能缺,否則將不是一個合法的 json 序列,會返回錯誤。

問題二:如果結構體的 json tag 定義與 key 大小不一致,可以反序列化成功嗎?

cert := struct {
    Username string `json:"username"`
    Password string `json:"password"`
}{}
err = json.Unmarshal([]byte(`{"UserName":"root","passWord":"123456"}`), &cert)
if err != nil {
    fmt.Println("err =", err)
} else {
    fmt.Println("username =", cert.Username)
    fmt.Println("password =", cert.Password)
}
// 輸出:
// username = root
// password = 123456

解:如果遇到大小寫問題,標準庫會儘可能地進行大小寫轉換,即:一個 key 與結構體中的定義不同,但忽略大小寫後是相同的,那麼依然能夠為欄位賦值。

為什麼使用第三方庫,標準庫有哪些不足?

Go json 標準庫 encoding/json[1]已經是提供了足夠舒適的 json 處理工具,廣受 Go 開發者的好評,但還是存在以下兩點問題:
  • API 不夠靈活:如沒有提供按需載入機制等;

  • 性能不太高:標準庫大量使用反射獲取值,首先 Go 的反射本身性能較差,較耗費 CPU 配置;其次頻繁分配對象,也會帶來記憶體分配和 GC 的開銷;
基於上面的考量,業務會根據使用場景、降本收益等訴求,引入合適的第三方庫。

三方庫哪些家強?

首先,思考從提問開始:
  • 熱門的三方庫有哪些?
  • 內部實現原理是什麼?
  • 如何結合業務去選型?

下麵是我收集整理的一些開源第三方庫,也歡迎有興趣的小伙伴一起交流補充!
庫名 encoder decoder compatible star 數 (2023.04.19) 社區維護性
StdLib(encoding/json)[2] ✔️ ✔️ N/A - -
FastJson(valyala/fastjson)[3] ✔️ ✔️ 1.9k 較差
GJson(tidwall/gjson)[4] ✔️ ✔️ 12.1k 較好
JsonParser(buger/jsonparser)[5] ✔️ ✔️ 5k 較差
JsonIter(json-iterator/go)[6] ✔️ ✔️ 部分相容 12.1k 較差
GoJson(goccy/go-json)[7] ✔️ ✔️ ✔️ 2.2k 較好
EasyJson(mailru/easyjson)[8] ✔️ ✔️ 4.1k 較差
Sonic(bytedance/sonic)[9] ✔️ ✔️ ✔️ 4.1k 較好

評判標準

評判標準包含三個維度:

  • 性能:內部實現原理是什麼,是否使用反射機制;

  • 穩定性:考慮到要投入生產使用,必須是一個較為穩定的三方庫;

  • 功能靈活性:是否支持 Unmarshal 到 map 或 struct,是否提供的一些定製化抽取的 API;
在功能劃分上,根據主流 json 庫 API,將它們的使用方式分為三種:
  • 泛型(generic)編解碼:json 沒有對應的 schema,只能依據自描述語義將讀取到的 value 解釋為對應語言的運行時對象,例如:json object 轉化為 Go map[string]interface{};

  • 定型(binding)編解碼:json 有對應的 schema,可以同時結合模型定義(Go struct)與 json 語法,將讀取到的 value 綁定到對應的模型欄位上去,同時完成數據解析與校驗;

  • 查找(get)& 修改(set):指定某種規則的查找路徑(一般是 key 與 index 的集合),獲取需要的那部分 json value 並處理。

功能評測

圖片

特點分析

圖片

性能評測

下麵是評測性能時所用的各個包的版本情況,具體的測試代碼,可參考benchmark_test [10]。

圖片

為什麼沒有評測 GJson 庫呢?
GJson 在單鍵查找的場景下有很大的優勢。這是因為它的查找是通過 lazy-load 實現的,它巧妙地跳過了傳遞值,有效地減少了很多不必要的解析,但其實跳過也是一種輕量級解析,實際是在處理 json 控制字元“[”、“{”等;然而當涉及到多鍵查找時,Gjson 做的事比標準庫更糟糕,這是其跳過機制的副作用:相同路徑查找導致的重覆開銷。針對它的使用場景,單純的評測定性編解碼和泛型編解碼,對 GJson 是不公平的。
根據樣本 json 的 key 數量和深度分為三個量級:
  • Small[11](400B, 11 keys, 3 layers)
  • Medium[12](13KB, 300+ key, 6 layers)
  • Large[13](635KB, 10000+ key, 6 layers)

評測過程中,需要註意不但要區分泛型編/解碼、定型編/解碼,而且也要考慮到併發情況下的性能表現,測試代碼樣例如下:
func BenchmarkEncoder_Generic_StdLib(b *testing.B) {
    _, _ = json.Marshal(_GenericValue)
    b.SetBytes(int64(len(TwitterJson)))
    b.ResetTimer()
for i := 0; i < b.N; i++ {
        _, _ = json.Marshal(_GenericValue)
    }
}
func BenchmarkEncoder_Binding_StdLib(b *testing.B) {
    _, _ = json.Marshal(&_BindingValue)
    b.SetBytes(int64(len(TwitterJson)))
    b.ResetTimer()
for i := 0; i < b.N; i++ {
        _, _ = json.Marshal(&_BindingValue)
    }
}
func BenchmarkEncoder_Parallel_Generic_StdLib(b *testing.B) {
    _, _ = json.Marshal(_GenericValue)
    b.SetBytes(int64(len(TwitterJson)))
    b.ResetTimer()
    b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
            _, _ = json.Marshal(_GenericValue)
        }
    })
}
func BenchmarkEncoder_Parallel_Binding_StdLib(b *testing.B) {
    _, _ = json.Marshal(&_BindingValue)
    b.SetBytes(int64(len(TwitterJson)))
    b.ResetTimer()
    b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
            _, _ = json.Marshal(&_BindingValue)
        }
    })
}

具體的指標數據和統計結果,可參考benchmark_readme [14],總體結論如下:

圖片


常見優化思路有哪些?

定型編解碼

對於有 schema 的定型編解碼場景而言,很多運算其實不需要在“運行時”執行。例如業務模型中確定了某個 json key 的值一定是布爾類型,其實可以在序列化階段直接輸出這個對象對應的 json 值(true/false),並不需要再檢查這個對象的具體類型。其核心思想是優化數據處理邏輯,將模型解釋與數據處理邏輯分離,讓前者提前固定下來,進而消除反射,提升性能。

動態函數組裝

JsonIter 就使用函數組裝模式,具體方法是將結構解釋為逐個欄位的編碼和解碼函數,然後將其組裝並緩存為整個對象對應的編解碼器,運行時再載入出來處理 json,將反射的性能損失成本降到最低。
這樣優化是否就能一勞永逸呢?
並沒有。因為這樣實現會轉化為大量的介面封裝和函數調用棧。在實際測試中,會發現隨著 json 數據量級的增長,因為調用介面涉及動態定址,彙編函數不能被內聯,而 Golang 的函數調用性能很差(沒有逐個寄存器的參數傳遞),函數調用的開銷也會成倍放大。

減少函數調用

為了避免動態彙編的函數調用開銷,業界實現方式目前主要有兩種:code-gen(代碼生成 ) 和 JIT( 即時編譯)。

code-gen

EasyJson 就採用代碼生成的思路。這種實現方式庫開發者實現起來相對簡單,性能高;但是它伴隨著模式依賴性和便利性的損失,增加業務代碼的維護成本和局限性,無法做到秒級熱更新,這也是代碼生成方式的 json 庫受眾並不廣泛的原因。

JIT

即時編譯將編譯過程移到了程式的載入或首次解析階段,只需要提供 json schema 對應的結構體類型信息,就可以一次性編譯生成對應的編解碼器,通常以 Golang 函數的形式緩存到堆外記憶體,便於後期高效執行。
// 函數緩存
type cache struct {
  functions map[*rtype]function
lock      sync.Mutex
}
var (
global = func() [caches]*cache {
var caches [caches]*cache
for idx := range caches {
      caches[idx] = &cache{functions: make(map[*rtype]function, 4)}
    }
return caches
  }()
)
func load(typ *rtype) (function, bool) {
do, ok := global[uintptr(unsafe.Pointer(typ))%caches].functions[typ]
return do, ok
}
func save(typ *rtype, do function) {
cache := global[uintptr(unsafe.Pointer(typ))%caches]
  cache.lock.Lock()
  cache.functions[typ] = do
  cache.lock.Unlock()
}

泛型編解碼

其實泛型編解碼性能差不只是因為沒有 schema ,因為可以對比一下 C++ 的 json 庫,如 simdjson[15],它的解析方式都是泛型的,但性能仍然很好;
Go 標準庫泛型解析性能差,原因在於它採用了 map[string]interface{} 作為 json 的編解碼對象。這其實是一種糟糕的選擇,原因如下:
  • 數據反序列化的過程中,map 插入的開銷很高;
  • 在數據序列化過程中,map 遍歷也遠不如數組高效;

如果用一種與 json AST 更貼近的數據結構來描述,不但可以讓轉換過程更加簡單,甚至可以實現 lazy-load 。

復用編碼緩衝區

通過使用 sync.Pool 復用先前編碼的緩衝區,可以有效減少 encode buffer 的記憶體分配次數。

type buffer struct {
    data []byte
}
var bufPool = sync.Pool{
    New: func() interface{} {
return &buffer{data: make([]byte, 0, 1024)}
    },
}
// 復用緩衝區
buf := bufPool.Get().(*buffer)
data := encode(buf.data)
newBuf := make([]byte, len(data))
copy(newBuf, buf)
buf.data = data
bufPool.Put(buf)

Sonic 庫為什麼性能好?

原理調研

Sonic 基於彙編進行開發,通過充分利用向量化(SIMD)指令、優化記憶體佈局和按需解析等關鍵技術,大幅提高了序列化反序列化性能。
它的優化思路可以分成離線和線上:
  • 離線場景:針對 Go 語言編譯優化的不足,Sonic 核心計算函數使用 C 語言編寫,使用 Clang 的深度優化編譯選項,並開發了一套 asm2asm 工具,將完全優化的 x86 彙編翻譯成 plan9 彙編,載入到 Golang 運行時,以供調用。

  • 線上場景:通過自定義 AST,實現了按需載入;採用 JIT 技術在運行時對模式對應的操作碼進行裝配,以 Golang 函數的形式緩存到堆外記憶體。這樣大大減少函數調用,同時也保證靈活性;

圖片

為什麼不使用 CGO ?
雖然使用 CGO 實現更加簡便, 但 CGO 在調用 c 代碼的時候引入了調度、切換線程棧等開銷,會造成較大(有的場景中高達 20 多倍)的性能損耗,無法對代碼進行了深度優化。

SIMD

什麼是 SIMD?
SIMD(Single-Instruction-Multi-Data 單指令流多數據流)它是一種採用一個控制器來控制多個處理器,同時對一組數據中的每一個數據分別執行相同的操作,從而實現空間上的並行性技術。例如:X86 的 SSE 或者 AVX2 指令集,以及 ARM 的 NEON 指令集等。它作為一組特殊的 CPU 指令,用於矢量數據的並行處理。目前,它被大多數 CPU 所支持,並廣泛用於圖像處理和大數據計算中,當然在 json 處理中也很有用。
SIMD 在 json 處理中解決了什麼問題?
對於 json 文本的處理與計算。其中一些問題在業界已經有比較成熟高效的解決方案,如浮點數轉字元串演算法 Ryu,整數轉字元串的查表法等;針對一些問題邏輯相對簡單,但是可能會面對較大數量級的文本,如 json string 的 unquote\quote 處理、空白字元的跳過等,也需要某種技術手段來提升處理能力,而 SIMD 就是一種用於並行處理大規模數據的技術。simdjson-go [16]在大型 json 場景(>100KB)中非常有競爭力。然而,對於一些極小的或不規則的字元串,SIMD 所需的額外負載操作將導致性能下降。因此, 對於大數據和小數據並存的實際場景,採用預設條件判斷(字元串大小、浮動精度等),將 SIMD 和標量指令結合起來,以達到最佳適應性。

asm2asm

為了提高執行效率,Sonic 中一些關鍵的計算函數是用 C 編寫的,用 Clang 編譯的;但由於 Clang 編譯出來的是 x86 彙編,而 Golang 編譯出來的是 plan9 彙編;那如何將優化後的彙編嵌入 Golang 中就變成了一個問題,因此為了在 Golang 中調用 Clang 編譯出來的彙編,位元組開發了一個內部工具 tools/asm2asm 將 x86 的彙編轉換為 plan9。

JIT 彙編

Sonic 借鑒 JsonIter 的組裝各類型處理函數的實現,針對編解碼器動態裝配的函數調用開銷,採用 JIT 技術在運行時對模式對應的操作碼(asm)進行裝配,最後以 Golang 函數的形式緩存到堆外記憶體。因為編譯後的編解碼函數是一個集成函數,可以大大減少函數調用,同時保證靈活性;對於 json 對應的結構體已知的服務場景,線上進行 JIT 彙編是比較耗時的,會導致首次請求耗時較高,也可以預先生成好彙編後的位元組碼。

RCU cache

為了提升 Codec Cache 的載入速度,每個結構體對應的序列化/反序列化位元組碼可以緩存起來,後面直接調用,以減少運行時彙編操作的執行次數(緩存足夠大的時候只需要執行一次)。Sync.Map 最初是用來緩存編解碼器的,但對於準靜態(讀遠多於寫)、較少元素(通常不超過幾十個)的場景來說,其性能並不理想,所以用“開放定址哈希+RCU 技術”重新實現了一個高性能和併發安全的緩存。

自定義 AST

針對泛型編解碼,基於 map 開銷較大的考慮,Sonic 實現了更符合 json 結構的樹形 AST;通過自定義的一種通用的泛型數據容器 sonic-ast 替代 Go interface,從而提升性能。

用 node {type, length, pointer} 表示任意一個 json 數據節點,並結合樹與數組結構描述節點之間的層級關係。針對部分解析,考慮到解析和跳過之間的巨大速度差距,將 lazy-load 機制到 AST 解析器中,以一種更加自適應和高效的方式來減少多鍵查詢的開銷。


type Node struct {
v int64
t types.ValueType
p unsafe.Pointer
}

如何實現部分解析?

sonic-ast 實現了一種有狀態、可伸縮的 json 解析過程。當使用者 get 某個 key 時,採用 skip 計算來輕量化跳過要獲取的 key 之前的 json 文本;對於該 key 之後的 json 節點,直接不做任何的解析處理;僅使用者真正需要的 key 才完全解析。

如何解決相同路徑查找重覆開銷的問題?

在對於子節點 skip 處理過程增加了一個步驟,將跳過 json 的 key、起始位、結束位記錄下來,分配一個 Raw-JSON 類型的節點保存下來,這樣二次 skip 就可以直接基於節點的 offset 進行,這樣解決相同路徑查找導致的重覆開銷的問題。同時 sonic-ast 支持了節點的更新、插入和序列化,甚至支持將任意 Go types 轉為節點並保存下來。

圖片

函數調用優化

  • 無棧記憶體管理:自己維護變數棧(記憶體池),避免 Go 函數棧擴展。

  • 自動生成跳轉表,加速 generic decoding 的分支跳轉。

  • 使用寄存器傳參:儘量避免 memory load & store,將使用頻繁的變數放到固定的寄存器上,如:json buffer、結構體指針;

  • 重寫函數調用:由於彙編函數不能內聯到 Go 函數中,函數調用引入的開銷甚至會抵消 SIMD 帶來的性能提升,因此在 JIT 中重新實現了一組輕量級的函數調用(維護全局函數表+函數 offset)。

業務實踐

適用場景

由於 Sonic 優化的是 json 操作,所以在 json 操作的 cpu 開銷占比較大的服務場景中收益會比較明顯。比如網關、轉發和入口服務等。

快速試用

為了較小侵入地驗證 Sonic 會對服務產生的性能提升,評估是否值得切換。推薦使用brahma-adshonor/gohook [17]工具庫,內部大概實現是向被 hook 的函數地址中寫入跳轉指令,直接跳轉到新的函數地址。
使用方式:在 main 函數的入口處 hook 當前使用的 json 庫函數為 Sonic 中對等函數。hook 是函數級的,因此可以具體驗證具體函數的性能提升;當人出於對某些函數的不信任、或者自己有性能更優異或更穩定的實現,也可以部分函數使用 Sonic。但需要註意,它未經過生產環境驗證,建議僅測試使用。切記,線上變更時,一定要滿足“可監控”,“可灰度”、“可回滾”的三大原則。

import "github.com/brahma-adshonor/gohook"
func main() {
// 在main函數的入口hook當前使用的json庫(如encoding/json)
    gohook.Hook(json.Marshal, sonic.Marshal, nil)
    gohook.Hook(json.Unmarshal, sonic.Unmarshal, nil)
}

收益情況

截止 2022 年 1 月份,Sonic 已應用於抖音,今日頭條等服務,累計為位元組節省了數十萬核。下圖為位元組某服務使用 Sonic 後高峰時段的 cpu 占用核數對比(圖來源sonic :基於 JIT 技術的開源全場景高性能 JSON 庫)。在生產環境中,Sonic 中也驗證了良好的收益,服務高峰期占用核數減少將近三分之一:

圖片

同時我們將線上的一個服務,將 HTTP 查詢介面 JSON 序列化由標準庫切換為 Sonic 庫後,在相同 QPS 量級下,CPU 利用率下降了接近 3 個點,效果也還不錯。

圖片

使用事項

HTML Escape

標準庫中預設會開啟 html Escape,而 Sonic 出於性能損耗預設不開啟。

func TestEncode(t *testing.T) {
  data := map[string]string{"&&": "<>"}
// 標準庫
var w1 = bytes.NewBuffer(nil)
  enc1 := json.NewEncoder(w1)
  err := enc1.Encode(data)
  assert.NoError(t, err)
// Sonic 庫
var w2 = bytes.NewBuffer(nil)
  enc2 := encoder.NewStreamEncoder(w2)
  err = enc2.Encode(data)
  assert.NoError(t, err)
  fmt.Printf("%v%v", w1.String(), w2.String())
}
// 運行結果:
{"\u0026\u0026":"\u003c\u003e"}
{"&&":"<>"}

若有需要可以通過下麵方式開啟:


import "github.com/bytedance/sonic/encoder"
v := map[string]string{"&&":"<>"}
ret, err := encoder.Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":" \u003e"}}`
enc := encoder.NewStreamEncoder(w)
enc.SetEscapeHTML(true)
err := enc.Encode(obj)

大型模式問題

由於 Sonic 使用 golang-asm 作為 JIT 彙編器,這不太適合運行時編譯,因此首次運行大型模式可能會導致請求超時甚至處理 OOM。為了獲得更好的穩定性,建議在 Marshal()/Unmarshal()之前使用 Pretouch()來處理大型模式或緊湊記憶體應用程式。

import (
"reflect"
"github.com/bytedance/sonic"
"github.com/bytedance/sonic/option"
)
func init() {
var v HugeStruct
// For most large types (nesting depth <= option.DefaultMaxInlineDepth)
    err := sonic.Pretouch(reflect.TypeOf(v))
// with more CompileOption...
    err := sonic.Pretouch(reflect.TypeOf(v),
// If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth),
// you can set compile recursive loops in Pretouch for better stability in JIT.
        option.WithCompileRecursiveDepth(loop),
// For a large nested struct, try to set a smaller depth to reduce compiling time.
        option.WithCompileMaxInlineDepth(depth),
    )
}

key 排序

Sonic 在序列化時預設是不對 key 進行排序的。json 的規範也與順序無關,但若需要 json 是有序的,可以在序列化時選擇排序的配置,大約會帶來 10%的性能損耗。排序方法如下:

import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/encoder"
// Binding map only
m := map[string]interface{}{}
v, err := encoder.Encode(m, encoder.SortMapKeys)
// Or ast.Node.SortKeys() before marshal
var root := sonic.Get(JSON)
err := root.SortKeys()

暫不支持 arm 架構

現象:用 Mac M1 無法編譯成功,解決方案可參考 sonic-compatibility[19],在編譯時通過添加以下參數: GOARCH=amd64,可解決編譯失敗的問題,但依舊無法支持本地 Debug 操作;
官方在 issue172 [20]中曾表示因內部實現的原因,這個問題的確很難搞,但還是有望在 Sonic V2 大版本中支持。

總結

綜上,業務選型上需要根據具體情況、不同領域的業務使用場景和發展趨勢進行選擇,綜合考慮各方面因素。最適配業務的才是最好的! 例如:如果業務只是簡單的解析 http 請求返回的 json 串的部分欄位,並且欄位都是確定的,偶爾需要搜索功能,那 Gjson 是很不錯的選擇。
以下是一些個人觀點,僅供參考:
  • 不太推薦使用 Jsoniter 庫,原因在於: Go 1.8 之前,官方 Json 庫的性能就收到多方詬病。不過隨著 Go 版本的迭代,標準 json 庫的性能也越來越高,Jsonter 的性能優勢也越來越窄。如果希望有極致的性能,應該選擇 Easyjson 等方案而不是 Jsoniter,而且 Jsoniter 近年已經不活躍了。

  • 比較推薦使用 Sonic 庫,因不論從性能和功能總體而言,Sonic 的表現的確很亮眼;此外,通過瞭解 Sonic 的內部實現原理,提供一種對於 cpu 密集型操作優化的“野路子”,即:通過編寫高性能的 C 代碼並經過優化編譯後供 Golang 直接調用。其實並不新鮮,因為實際上 Go 源碼中的一些 cpu 密集型操作底層就是編譯成了彙編後使用的,如:crypto 和 math。

參考資料:

  • 深入 Go 中各個高性能 JSON 解析庫:https://www.luozhiyun.com/archives/535
  • Go 語言原生的 json 包有什麼問題?如何更好地處理 JSON 數據?:https://cloud.tencent.com/developer/article/1820473
  • go-json#how-it-works:https://github.com/goccy/go-json#how-it-works
  • sonic :基於 JIT 技術的開源全場景高性能 JSON 庫
  • Introduction to Sonic:https://github.com/bytedance/sonic/blob/main/INTRODUCTION.md
  • bytedance/sonic-readme:https://pkg.go.dev/github.com/bytedance/[email protected]#section-readme
  • 為位元組節省數十萬核的 json 庫 sonic:https://zhuanlan.zhihu.com/p/586050976

[1]https://pkg.go.dev/encoding/json?spm=ata.21736010.0.0.6e462b76wytEry

[2]https://pkg.go.dev/encoding/json?spm=ata.21736010.0.0.6e462b76wytEry

[3]https://github.com/valyala/fastjson?spm=ata.21736010.0.0.6e462b76wytEry

[4]https://github.com/tidwall/gjson?spm=ata.21736010.0.0.6e462b76wytEry

[5]https://github.com/buger/jsonparser?spm=ata.21736010.0.0.6e462b76wytEry

[6]https://github.com/json-iterator/go?spm=ata.21736010.0.0.6e462b76wytEry

[7]https://github.com/goccy/go-json?spm=ata.21736010.0.0.6e462b76wytEry

[8]https://github.com/mailru/easyjson?spm=ata.21736010.0.0.6e462b76wytEry

[9]https://github.com/bytedance/sonic?spm=ata.21736010.0.0.6e462b76wytEry

[10]https://github.com/zhao520a1a/go-base/blob/master/json/benchmark_test/bench.sh?spm=ata.21736010.0.0.6e462b76wytEry&file=bench.sh

[11]https://github.com/zhao520a1a/go-base/blob/master/json/testdata/small.go?spm=ata.21736010.0.0.6e462b76wytEry&file=small.go

[12]https://github.com/zhao520a1a/go-base/blob/master/json/testdata/medium.go?spm=ata.21736010.0.0.6e462b76wytEry&file=medium.go

[13]https://github.com/zhao520a1a/go-base/blob/master/json/testdata/large.json?spm=ata.21736010.0.0.6e462b76wytEry&file=large.json

[14]https://github.com/zhao520a1a/go-base/blob/master/json/benchmark_test/README.md?spm=ata.21736010.0.0.6e462b76wytEry&file=README.md

[15]https://github.com/simdjson/simdjson?spm=ata.21736010.0.0.6e462b76wytEry

[16]https://github.com/minio/simdjson-go?spm=ata.21736010.0.0.6e462b76wytEry

[17]https://github.com/brahma-adshonor/gohook?spm=ata.21736010.0.0.6e462b76wytEry

[19]https://github.com/bytedance/sonic?spm=ata.21736010.0.0.6e462b76wytEry#compatibility

[20]https://github.com/bytedance/sonic/issues/172?spm=ata.21736010.0.0.6e462b76wytEry

 

作者|趙金鑫(筆話)

本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/Which-is-the-strongest-third-party-package-for-Go-JSON.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 前言 最近在開發文件存儲服務,需要符合s3的協議標準,可以直接接入aws-sdk,本文針對sdk發出請求的鑒權信息進行重新組合再簽名驗證有效性,sdk版本如下 ```xml software.amazon.awssdk s3 2.20.45 ``` # 演算法解析 首先對V4版本簽名演算法的數據結構 ...
  • # Rust Web 全棧開發之編寫 WebAssembly 應用 MDN Web Docs: 官網: ## 項目結構 和 功能 **Web App 教師註冊 WebService WebAssembly App 課程管理** ## 什麼是 WebAssembly - WebAssembly 是一種 ...
  • # FileInputStream 和 FileOutputStream ![](https://img2023.cnblogs.com/blog/3008601/202306/3008601-20230604102221520-1382311786.png) - InputStream:位元組輸入流 ...
  • # IO流原理及流的分類 ### 一、Java IO流原理 1. I/O是Input/Output的縮寫,I/O技術是非常實用的技術,用於處理數據傳輸。如讀/寫文件,網路通訊等。 2. Java程式中,對於數據的輸入/輸出操作以”流(stream)“的方式進行。 3. java.io包下提供了各種” ...
  • [模板傳送](https://www.luogu.com.cn/problem/P3369) FHQ-Treap顧名思義就是範浩強大佬設計的一種二叉平衡樹 下麵我們來講一下它的原理和代碼 # 結構體 對於一個節點,我們需要記錄的是 * 對應的值 * 子樹節點數 * 左右孩子編號 * 對應的隨機值 ` ...
  • # 文件的基本使用 ### 一、文件 - **什麼是文件** 文件是保存數據的地方,比如word文檔,txt文件,excel文件……都是文件。即可以保存一張圖片,也可以保持視頻,聲音…… - **文件流** 文件在程式中是以流的形式來操作的 ![文件流](https://img2023.cnblog ...
  • groovy 3.0.7 ### DES加密簡介 加密分為對稱加密和非對稱加密。非對稱加密,加解密使用不同的密鑰,如RSA;對稱加密,加解密使用相同的密鑰,如DES(Data Encryption Standard,即數據加密標準)。相對而言,非對稱加密安全性更高,但是計算過程複雜耗時,一般只應用於 ...
  • # 1.列表切片 前面學習的是如何處理列表的所有數據元素。python還可以處理列表的部分元素,python稱之為切片。 ## 1.1創建切片 創建切片,可指定要使用的第一個數據元素的索引和最後一個數據元素的索引。與range函數一樣,python在到達指定的第二個索引前面的數據元素後停止。比如要輸 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...