bytedance/sonic
GitHub: bytedance/sonic
Sonic 是一个基于 Go 的高性能 JSON 序列化与反序列化库,通过 JIT 和 SIMD 技术大幅提升 JSON 处理速度。
Stars: 9482 | Forks: 452
# Sonic
English | [中文](README_ZH_CN.md)
一个极其快速的 JSON 序列化与反序列化库,通过 JIT(即时编译)和 SIMD(单指令多数据)技术加速。
## 要求
- Go: 1.18~1.26
- 注意:由于 [问题](https://github.com/golang/go/issues/71672),Go 1.24.0 不受支持;请使用更高的 Go 版本或通过构建标志 `-ldflags="-checklinkname=0"`。
- 操作系统:Linux / MacOS / Windows
- CPU:AMD64 / (ARM64,需要 go1.20 以上版本)
## 特性
- 运行时对象绑定,无需代码生成
- 完整的 JSON 值操作 API
- 快,快,快!
## API
参见 [go.dev](https://pkg.go.dev/github.com/bytedance/sonic)
## 基准测试
对于 **所有大小** 的 JSON 和 **所有场景** 的使用,**Sonic 表现最佳**。
- [中等大小](https://github.com/bytedance/sonic/blob/main/decoder/testdata_test.go#L19) (13KB, 300+ 键, 6 层)
```
goversion: 1.17.1
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkEncoder_Generic_Sonic-16 32393 ns/op 402.40 MB/s 11965 B/op 4 allocs/op
BenchmarkEncoder_Generic_Sonic_Fast-16 21668 ns/op 601.57 MB/s 10940 B/op 4 allocs/op
BenchmarkEncoder_Generic_JsonIter-16 42168 ns/op 309.12 MB/s 14345 B/op 115 allocs/op
BenchmarkEncoder_Generic_GoJson-16 65189 ns/op 199.96 MB/s 23261 B/op 16 allocs/op
BenchmarkEncoder_Generic_StdLib-16 106322 ns/op 122.60 MB/s 49136 B/op 789 allocs/op
BenchmarkEncoder_Binding_Sonic-16 6269 ns/op 2079.26 MB/s 14173 B/op 4 allocs/op
BenchmarkEncoder_Binding_Sonic_Fast-16 5281 ns/op 2468.16 MB/s 12322 B/op 4 allocs/op
BenchmarkEncoder_Binding_JsonIter-16 20056 ns/op 649.93 MB/s 9488 B/op 2 allocs/op
BenchmarkEncoder_Binding_GoJson-16 8311 ns/op 1568.32 MB/s 9481 B/op 1 allocs/op
BenchmarkEncoder_Binding_StdLib-16 16448 ns/op 792.52 MB/s 9479 B/op 1 allocs/op
BenchmarkEncoder_Parallel_Generic_Sonic-16 6681 ns/op 1950.93 MB/s 12738 B/op 4 allocs/op
BenchmarkEncoder_Parallel_Generic_Sonic_Fast-16 4179 ns/op 3118.99 MB/s 10757 B/op 4 allocs/op
BenchmarkEncoder_Parallel_Generic_JsonIter-16 9861 ns/op 1321.84 MB/s 14362 B/op 115 allocs/op
BenchmarkEncoder_Parallel_Generic_GoJson-16 18850 ns/op 691.52 MB/s 23278 B/op 16 allocs/op
BenchmarkEncoder_Parallel_Generic_StdLib-16 45902 ns/op 283.97 MB/s 49174 B/op 789 allocs/op
BenchmarkEncoder_Parallel_Binding_Sonic-16 1480 ns/op 8810.09 MB/s 13049 B/op 4 allocs/op
BenchmarkEncoder_Parallel_Binding_Sonic_Fast-16 1209 ns/op 10785.23 MB/s 11546 B/op 4 allocs/op
BenchmarkEncoder_Parallel_Binding_JsonIter-16 6170 ns/op 2112.58 MB/s 9504 B/op 2 allocs/op
BenchmarkEncoder_Parallel_Binding_GoJson-16 3321 ns/op 3925.52 MB/s 9496 B/op 1 allocs/op
BenchmarkEncoder_Parallel_Binding_StdLib-16 3739 ns/op 3486.49 MB/s 9480 B/op 1 allocs/op
BenchmarkDecoder_Generic_Sonic-16 66812 ns/op 195.10 MB/s 57602 B/op 723 allocs/op
BenchmarkDecoder_Generic_Sonic_Fast-16 54523 ns/op 239.07 MB/s 49786 B/op 313 allocs/op
BenchmarkDecoder_Generic_StdLib-16 124260 ns/op 104.90 MB/s 50869 B/op 772 allocs/op
BenchmarkDecoder_Generic_JsonIter-16 91274 ns/op 142.81 MB/s 55782 B/op 1068 allocs/op
BenchmarkDecoder_Generic_GoJson-16 88569 ns/op 147.17 MB/s 66367 B/op 973 allocs/op
BenchmarkDecoder_Binding_Sonic-16 32557 ns/op 400.38 MB/s 28302 B/op 137 allocs/op
BenchmarkDecoder_Binding_Sonic_Fast-16 28649 ns/op 455.00 MB/s 24999 B/op 34 allocs/op
BenchmarkDecoder_Binding_StdLib-16 111437 ns/op 116.97 MB/s 10576 B/op 208 allocs/op
BenchmarkDecoder_Binding_JsonIter-16 35090 ns/op 371.48 MB/s 14673 B/op 385 allocs/op
BenchmarkDecoder_Binding_GoJson-16 28738 ns/op 453.59 MB/s 22039 B/op 49 allocs/op
BenchmarkDecoder_Parallel_Generic_Sonic-16 12321 ns/op 1057.91 MB/s 57233 B/op 723 allocs/op
BenchmarkDecoder_Parallel_Generic_Sonic_Fast-16 10644 ns/op 1224.64 MB/s 49362 B/op 313 allocs/op
BenchmarkDecoder_Parallel_Generic_StdLib-16 57587 ns/op 226.35 MB/s 50874 B/op 772 allocs/op
BenchmarkDecoder_Parallel_Generic_JsonIter-16 38666 ns/op 337.12 MB/s 55789 B/op 1068 allocs/op
BenchmarkDecoder_Parallel_Generic_GoJson-16 30259 ns/op 430.79 MB/s 66370 B/op 974 allocs/op
BenchmarkDecoder_Parallel_Binding_Sonic-16 5965 ns/op 2185.28 MB/s 27747 B/op 137 allocs/op
BenchmarkDecoder_Parallel_Binding_Sonic_Fast-16 5170 ns/op 2521.31 MB/s 24715 B/op 34 allocs/op
BenchmarkDecoder_Parallel_Binding_StdLib-16 27582 ns/op 472.58 MB/s 10576 B/op 208 allocs/op
BenchmarkDecoder_Parallel_Binding_JsonIter-16 13571 ns/op 960.51 MB/s 14685 B/op 385 allocs/op
BenchmarkDecoder_Parallel_Binding_GoJson-16 10031 ns/op 1299.51 MB/s 22111 B/op 49 allocs/op
BenchmarkGetOne_Sonic-16 3276 ns/op 3975.78 MB/s 24 B/op 1 allocs/op
BenchmarkGetOne_Gjson-16 9431 ns/op 1380.81 MB/s 0 B/op 0 allocs/op
BenchmarkGetOne_Jsoniter-16 51178 ns/op 254.46 MB/s 27936 B/op 647 allocs/op
BenchmarkGetOne_Parallel_Sonic-16 216.7 ns/op 60098.95 MB/s 24 B/op 1 allocs/op
BenchmarkGetOne_Parallel_Gjson-16 1076 ns/op 12098.62 MB/s 0 B/op 0 allocs/op
BenchmarkGetOne_Parallel_Jsoniter-16 17741 ns/op 734.06 MB/s 27945 B/op 647 allocs/op
BenchmarkSetOne_Sonic-16 9571 ns/op 1360.61 MB/s 1584 B/op 17 allocs/op
BenchmarkSetOne_Sjson-16 36456 ns/op 357.22 MB/s 52180 B/op 9 allocs/op
BenchmarkSetOne_Jsoniter-16 79475 ns/op 163.86 MB/s 45862 B/op 964 allocs/op
BenchmarkSetOne_Parallel_Sonic-16 850.9 ns/op 15305.31 MB/s 1584 B/op 17 allocs/op
BenchmarkSetOne_Parallel_Sjson-16 18194 ns/op 715.77 MB/s 52247 B/op 9 allocs/op
BenchmarkSetOne_Parallel_Jsoniter-16 33560 ns/op 388.05 MB/s 45892 B/op 964 allocs/op
BenchmarkLoadNode/LoadAll()-16 11384 ns/op 1143.93 MB/s 6307 B/op 25 allocs/op
BenchmarkLoadNode_Parallel/LoadAll()-16 5493 ns/op 2370.68 MB/s 7145 B/op 25 allocs/op
BenchmarkLoadNode/Interface()-16 17722 ns/op 734.85 MB/s 13323 B/op 88 allocs/op
BenchmarkLoadNode_Parallel/Interface()-16 10330 ns/op 1260.70 MB/s 15178 B/op 88 allocs/op
```
- [小型](https://github.com/bytedance/sonic/blob/main/testdata/small.go) (400B, 11 键, 3 层)

- [大型](https://github.com/bytedance/sonic/blob/main/testdata/twitter.json) (635KB, 10000+ 键, 6 层)

基准测试代码见 [bench.sh](https://github.com/bytedance/sonic/blob/main/scripts/bench.sh)。
## 工作原理
参见 [INTRODUCTION.md](./docs/INTRODUCTION.md)。
## 使用方法
### 序列化/反序列化
默认行为在很大程度上与 `encoding/json` 一致,除了 HTML 转义形式(参见 [转义 HTML](https://github.com/bytedance/sonic/blob/main/README.md#escape-html))和 `SortKeys` 功能(可选支持,见 [排序键](https://github.com/bytedance/sonic/blob/main/README.md#sort-keys)),这**不**符合 [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259)。
```
import "github.com/bytedance/sonic"
var data YourSchema
// Marshal
output, err := sonic.Marshal(&data)
// Unmarshal
err := sonic.Unmarshal(output, &data)
```
### 流式 IO
Sonic 支持从 `io.Reader` 解码 JSON 或将对象编码到 `io.Writer`,旨在处理多个值以及减少内存消耗。
- 编码器
```
var o1 = map[string]interface{}{
"a": "b",
}
var o2 = 1
var w = bytes.NewBuffer(nil)
var enc = sonic.ConfigDefault.NewEncoder(w)
enc.Encode(o1)
enc.Encode(o2)
fmt.Println(w.String())
// Output:
// {"a":"b"}
// 1
```
- 解码器
```
var o = map[string]interface{}{}
var r = strings.NewReader(`{"a":"b"}{"1":"2"}`)
var dec = sonic.ConfigDefault.NewDecoder(r)
dec.Decode(&o)
dec.Decode(&o)
fmt.Printf("%+v", o)
// Output:
// map[1:2 a:b]
```
### 使用 Number/Int64
```
import "github.com/bytedance/sonic/decoder"
var input = `1`
var data interface{}
// default float64
dc := decoder.NewDecoder(input)
dc.Decode(&data) // data == float64(1)
// use json.Number
dc = decoder.NewDecoder(input)
dc.UseNumber()
dc.Decode(&data) // data == json.Number("1")
// use int64
dc = decoder.NewDecoder(input)
dc.UseInt64()
dc.Decode(&data) // data == int64(1)
root, err := sonic.GetFromString(input)
// Get json.Number
jn := root.Number()
jm := root.InterfaceUseNumber().(json.Number) // jn == jm
// Get float64
fn := root.Float64()
fm := root.Interface().(float64) // jn == jm
```
### 排序键
由于排序带来的性能损失(大约 10%),Sonic 默认不启用此功能。如果你的组件依赖它来工作(如 [zstd](https://github.com/facebook/zstd)),请这样使用:
```
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()
```
### 转义 HTML
由于性能损失(大约 15%),Sonic 默认不启用此功能。你可以使用 `encoder.EscapeHTML` 选项来开启此功能(与 `encoding/json.HTMLEscape` 对齐)。
```
import "github.com/bytedance/sonic"
v := map[string]string{"&&":"<>"}
ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"}}`
```
### 紧凑格式
Sonic 默认将原始对象(struct/map 等)编码为紧凑格式的 JSON,但在序列化 `json.RawMessage` 或 `json.Marshaler` 时除外:Sonic 会确保验证其输出的 JSON 是否有效,但出于性能考虑**不会**对其进行紧凑化。我们提供了 `encoder.CompactMarshaler` 选项来添加紧凑化处理。
### 打印错误
如果输入 JSON 中存在无效语法,Sonic 将返回 `decoder.SyntaxError`,它支持错误位置的精美打印。
```
import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/decoder"
var data interface{}
err := sonic.UnmarshalString("[[[}]]", &data)
if err != nil {
/* One line by default */
println(e.Error()) // "Syntax error at index 3: invalid char\n\n\t[[[}]]\n\t...^..\n"
/* Pretty print */
if e, ok := err.(decoder.SyntaxError); ok {
/*Syntax error at index 3: invalid char
[[[}]]
...^..
*/
print(e.Description())
} else if me, ok := err.(*decoder.MismatchTypeError); ok {
// decoder.MismatchTypeError is new to Sonic v1.6.0
print(me.Description())
}
}
```
#### 类型不匹配 [Sonic v1.6.0]
如果给定键的值存在**类型不匹配**,Sonic 将报告 `decoder.MismatchTypeError`(如果存在多个,则报告最后一个),但仍会跳过错误的值并继续解码下一个 JSON。
```
import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/decoder"
var data = struct{
A int
B int
}{}
err := UnmarshalString(`{"A":"1","B":1}`, &data)
println(err.Error()) // Mismatch type int with value string "at index 5: mismatched type with value\n\n\t{\"A\":\"1\",\"B\":1}\n\t.....^.........\n"
fmt.Printf("%+v", data) // {A:0 B:1}
```
### Ast.Node
Sonic/ast.Node 是一个完全自包含的 JSON AST。它实现了序列化和反序列化,并提供了强大的 API 来获取和修改通用数据。
#### 获取/索引
通过给定路径搜索部分 JSON,路径必须是非负整数或字符串,或 nil
```
import "github.com/bytedance/sonic"
input := []byte(`{"key1":[{},{"key2":{"key3":[1,2,3]}}]}`)
// no path, returns entire json
root, err := sonic.Get(input)
raw := root.Raw() // == string(input)
// multiple paths
root, err := sonic.Get(input, "key1", 1, "key2")
sub := root.Get("key3").Index(2).Int64() // == 3
```
**提示**:由于 `Index()` 使用偏移量来定位数据,这比像 `Get()` 那样扫描要快得多,我们建议你尽可能使用它。Sonic 还提供了另一个 API `IndexOrGet()`,它底层也使用偏移量,同时确保键匹配。
#### 搜索选项
`Searcher` 提供了一些选项供用户满足不同需求:
```
opts := ast.SearchOption{ CopyReturn: true ... }
val, err := sonic.GetWithOptions(JSON, opts, "key")
```
- CopyReturn
指示搜索器复制结果 JSON 字符串,而不是从输入中引用。如果你缓存结果,这有助于减少内存使用。
- ConcurrentRead
由于 `ast.Node` 使用“懒加载”设计,默认不支持并发读取。如果你想并发读取它,请指定此选项。
- ValidateJSON
指示搜索器验证整个 JSON。此选项默认启用,会稍微降低搜索速度。
#### 设置/取消设置
通过 Set()/Unset() 修改 JSON 内容
```
import "github.com/bytedance/sonic"
// Set
exist, err := root.Set("key4", NewBool(true)) // exist == false
alias1 := root.Get("key4")
println(alias1.Valid()) // true
alias2 := root.Index(1)
println(alias1 == alias2) // true
// Unset
exist, err := root.UnsetByIndex(1) // exist == true
println(root.Get("key4").Check()) // "value not exist"
```
#### 序列化
要将 `ast.Node` 编码为 JSON,请使用 `MarshalJson()` 或 `json.Marshal()`(必须传递节点的指针)
```
import (
"encoding/json"
"github.com/bytedance/sonic"
)
buf, err := root.MarshalJson()
println(string(buf)) // {"key1":[{},{"key2":{"key3":[1,2,3]}}]}
exp, err := json.Marshal(&root) // WARN: use pointer
println(string(buf) == string(exp)) // true
```
#### API
- 验证:`Check()`, `Error()`, `Valid()`, `Exist()`
- 搜索:`Index()`, `Get()`, `IndexPair()`, `IndexOrGet()`, `GetByPath()`
- Go 类型转换:`Int64()`, `Float64()`, `String()`, `Number()`, `Bool()`, `Map[UseNumber|UseNode]()`, `Array[UseNumber|UseNode]()`, `Interface[UseNumber|UseNode]()`
- Go 类型包装:`NewRaw()`, `NewNumber()`, `NewNull()`, `NewBool()`, `NewString()`, `NewObject()`, `NewArray()`
- 迭代:`Values()`, `Properties()`, `ForEach()`, `SortKeys()`
- 修改:`Set()`, `SetByIndex()`, `Add()`
### Ast.Visitor
Sonic 提供了一个高级 API,用于将 JSON 完全解析为非标准类型(既不是 `struct` 也不是 `map[string]interface{}`),而不使用任何中间表示形式(`ast.Node` 或 `interface{}`)。例如,你可能有以下类似 `interface{}` 但实际上不是 `interface{}` 的类型:
```
type UserNode interface {}
// the following types implement the UserNode interface.
type (
UserNull struct{}
UserBool struct{ Value bool }
UserInt64 struct{ Value int64 }
UserFloat64 struct{ Value float64 }
UserString struct{ Value string }
UserObject struct{ Value map[string]UserNode }
UserArray struct{ Value []UserNode }
)
```
Sonic 提供以下 API 来返回 **JSON AST 的前序遍历**。`ast.Visitor` 是一个 SAX 风格的接口,在一些 C++ JSON 库中使用。你需要自己实现 `ast.Visitor` 并将其传递给 `ast.Preorder()` 方法。在你的 visitor 中,你可以使用自定义类型来表示 JSON 值。你的 visitor 中可能需要一个 O(n) 空间的容器(如栈)来记录对象/数组的层次结构。
```
func Preorder(str string, visitor Visitor, opts *VisitorOptions) error
type Visitor interface {
OnNull() error
OnBool(v bool) error
OnString(v string) error
OnInt64(v int64, n json.Number) error
OnFloat64(v float64, n json.Number) error
OnObjectBegin(capacity int) error
OnObjectKey(key string) error
OnObjectEnd() error
OnArrayBegin(capacity int) error
OnArrayEnd() error
}
```
详细用法见 [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go)。我们还在 [ast/visitor_test.go](https://github.com/bytedance/sonic/blob/main/ast/visitor_test.go) 中为 `UserNode` 实现了一个演示 visitor。
## 兼容性
对于希望在不同场景下使用 Sonic 的开发者,我们提供了一些集成配置 `sonic.API`
- `ConfigDefault`:Sonic 的默认配置 (`EscapeHTML=false`, `SortKeys=false`...),在快速运行 Sonic 的同时确保安全。
- `ConfigStd`:标准库兼容配置 (`EscapeHTML=true`, `SortKeys=true`...)
- `ConfigFastest`:最快的配置 (`NoQuoteTextMarshaler=true`),在 Sonic 上尽可能快地运行。
由于开发高性能代码的难度,Sonic **不能**保证支持所有环境。在不支持 Sonic 的环境中,实现将回退到 `encoding/json`。因此以下配置都将等于 `ConfigStd`。
## 提示
### 预处理
由于 Sonic 使用 [golang-asm](https://github.com/twitchyliquid64/golang-asm) 作为 JIT 汇编器,它**不太**适合运行时编译,对大型 schema 的首次运行可能导致请求超时甚至进程 OOM。为了更好的稳定性,我们建议**对大型 schema 或对延迟敏感的应用使用 `PretouchMany()`**,在 `Marshal()/Unmarshal()` 之前。
```
import (
"reflect"
"github.com/bytedance/sonic"
"github.com/bytedance/sonic/option"
)
func init() {
var v1 HugeStruct1
var v2 HugeStruct2
// For most large types (nesting depth <= option.DefaultMaxInlineDepth)
sonic.PretouchMany([]reflect.Type{reflect.TypeOf(v1), reflect.TypeOf(v2)},
// If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth),
// you can set more recursive loops in Pretouch for fully sufficient JIT.
option.WithCompileRecursiveDepth(loop),
// For a large struct, try to set a smaller depth to reduce compiling time.
option.WithCompileMaxInlineDepth(depth),
)
}
```
### 复制字符串
当解码**没有任何转义字符的字符串值**时,Sonic 会从原始 JSON 缓冲区引用它们,而不是分配新缓冲区进行复制。这对 CPU 性能帮助很大,但只要解码的对象正在使用,就可能将整个 JSON 缓冲区保留在内存中。在实践中,我们发现引用 JSON 缓冲区引入的额外内存通常是解码对象的 20% ~ 80%。一旦应用程序长时间持有这些对象(例如,缓存解码的对象以供重用),其服务器的使用内存可能会上升。
- `Config.CopyString` / `decoder.CopyString()`:我们为 `Decode()` / `Unmarshal()` 用户提供了选项,让他们可以选择不引用 JSON 缓冲区,这可能在一定程度上导致 CPU 性能下降。
- `GetFromStringNoCopy()`:出于内存安全考虑,`sonic.Get()` / `sonic.GetFromString()` 现在会复制返回的 JSON。如果用户希望更快地获取 JSON 并且不关心内存使用,可以使用 `GetFromStringNoCopy()` 直接返回从源引用的 JSON。
### 传递 string 还是 []byte?
为了与 `encoding/json` 对齐,我们提供了传递 `[]byte` 作为参数的 API,但同时出于安全考虑会执行字符串到字节的复制,当原始 JSON 很大时可能会损失性能。因此,你可以使用 `UnmarshalString()` 和 `GetFromString()` 来传递字符串,只要你的原始数据是字符串,或者对你的 []byte 进行 **nocopy-cast** 是安全的。我们还提供了 `MarshalString()` API,方便对编码后的 JSON []byte 进行**nocopy-cast**,这是安全的,因为 Sonic 的输出字节总是唯一且被复制的。
### 加速 `encoding.TextMarshaler`
为确保数据安全,`sonic.Encoder` 默认会对来自 `encoding.TextMarshaler` 接口的字符串值进行引号和转义处理,如果你的大部分数据都是这种形式,这可能会大幅降低性能。我们提供了 `encoder.NoQuoteTextMarshaler` 来跳过这些操作,这意味着你**必须**确保它们的输出字符串已经按照 [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259) 进行了转义和加引号。
### 通用数据的更好性能
在**完全解析**场景下,`Unmarshal()` 的性能优于 `Get()` + `Node.Interface()`。但如果你只有特定 JSON 的部分 schema,你可以将 `Get()` 和 `Unmarshal()` 结合使用:
```
import "github.com/bytedance/sonic"
node, err := sonic.GetFromString(_TwitterJson, "statuses", 3, "user")
var user User // your partial schema...
err = sonic.UnmarshalString(node.Raw(), &user)
```
即使你没有任何 schema,也请使用 `ast.Node` 作为通用值的容器,而不是 `map` 或 `interface`:
```
import "github.com/bytedance/sonic"
root, err := sonic.GetFromString(_TwitterJson)
user := root.GetByPath("statuses", 3, "user") // === root.Get("status").Index(3).Get("user")
err = user.Check()
// err = user.LoadAll() // only call this when you want to use 'user' concurrently...
go someFunc(user)
```
为什么?因为 `ast.Node` 使用 `array` 存储其子节点:
- 在插入(反序列化)和扫描(序列化)数据时,`Array` 的性能**远优于** `Map`;
- **哈希**(`map[x]`)的效率不如**索引**(`array[x]`),而 `ast.Node` 可以在**数组和对象**上都执行索引;
- 使用 `Interface()` / `Map()` 意味着 Sonic 必须解析所有底层值,而 `ast.Node` 可以**按需**解析它们。
**注意:** `ast.Node` 由于其**懒加载**设计,**不能**直接保证并发安全。但是,你可以调用 `Node.Load()` / `Node.LoadAll()` 来实现这一点,这可能会带来性能降低,但它仍然比转换为 `map` 或 `interface{}` 更快。
### 使用 Ast.Node 还是 Ast.Visitor?
对于通用数据,`ast.Node` 在大多数情况下应该能满足你的需求。
然而,`ast.Node` 是为部分处理 JSON 字符串而设计的。它有一些特殊设计,如懒加载,可能不适合直接解析整个 JSON 字符串(像 `Unmarshal()` 那样)。虽然 `ast.Node` 比 `map` 或 `interface{}` 更好,但如果你的最终类型是自定义的,它毕竟也是一种中间表示形式,解析后你仍需将上述类型转换为你的自定义类型。
为了获得更好的性能,在前一种情况下,`ast.Visitor` 将是更好的选择。它像 `Unmarshal()` 一样执行 JSON 解码,你可以直接使用你的最终类型来表示 JSON AST,无需任何中间表示。
但 `ast.Visitor` 并不是一个非常方便的 API。你可能需要编写大量代码来实现你的 visitor,并在解码过程中仔细维护树的层次结构。如果你决定使用此 API,请仔细阅读 [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) 中的注释。
### 缓冲区大小
Sonic 在许多地方(如 `encoder.Encode`、`ast.Node.MarshalJSON`)使用内存池来提高性能,当服务器负载较高时,这可能会产生更多的内存使用(占用)。参见 [问题 614](https://github.com/bytedance/sonic/issues/614)。因此,我们引入了一些选项让用户控制内存池的行为。参见 [option](https://pkg.go.dev/github.com/bytedance/sonic@v1.11.9/option#pkg-variables) 包。
### 更快的 JSON 跳过
出于安全考虑,Sonic 在解码原始 JSON 或编码 `json.Marshaler` 时使用 [FSM](native/skip_one.c) 算法验证 JSON,这比 [SIMD-searching-pair](native/skip_one_fast.c) 算法慢得多(1~10 倍)。如果用户有很多冗余的 JSON 值且**不需要**严格验证 JSON 的正确性,可以启用以下选项:
- `Config.NoValidateSkipJSON`:用于在解码时更快地跳过 JSON,例如未知字段、`json.Unmarshaler(json.RawMessage)`、类型不匹配的值和冗余的数组元素。
- `Config.NoValidateJSONMarshaler`:避免在编码 `json.Marshaler` 时验证 JSON。
- `SearchOption.ValidateJSON`:指示在 `Get` 时是否验证定位到的 JSON 值。
## JSON-Path 支持 (GJSON)
[tidwall/gjson](https://github.com/tidwall/gjson) 提供了一个全面且流行的 JSON-Path API,并且
很多旧代码严重依赖它。因此,我们提供了一个包装库,它将 gjson 的 API 与 Sonic 的 SIMD 算法结合起来,以提升性能。参见 [cloudwego/gjson](https://github.com/cloudwego/gjson)。
## 社区
Sonic 是 [CloudWeGo](https://www.cloudwego.io/) 的一个子项目。我们致力于构建云原生生态系统。
标签:API操作, EVTX分析, Go语言, JA3, JIT编译, JSON反序列化, JSON序列化, SIMD优化, 反序列化库, 多架构支持, 字节跳动, 序列化库, 库比较, 开源, 性能基准测试, 数据序列化, 日志审计, 程序破解, 计算优化, 运行时绑定, 速度优化