V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
taowen
V2EX  ›  Go 编程语言

比 encoding/json 更快地解析 json

  •  1
     
  •   taowen · 2016-12-05 00:06:56 +08:00 · 2236 次点击
    这是一个创建于 2936 天前的主题,其中的信息可能已经有所发展或是发生改变。

    https://github.com/json-iterator/go

    直接替换 json.Unmarshal

    仍然使用反射实现,但是比 encoding/json 的版本更快。完整的测试代码: https://github.com/json-iterator/go-benchmark

    func Benchmark_array_by_stardard_lib(b *testing.B) {
        b.ReportAllocs()
        for n := 0; n < b.N; n++ {
            sample := make([]int, 0, 10)
            json.Unmarshal([]byte(`[1,2,3,4,5,6,7,8,9]`), &sample)
        }
    }
    

    500000 2478 ns/op 408 B/op 14 allocs/op

    func Benchmark_array_by_jsoniter(b *testing.B) {
        b.ReportAllocs()
        for n := 0; n < b.N; n++ {
            sample := make([]int, 0, 10)
            jsoniter.Unmarshal([]byte(`[1,2,3,4,5,6,7,8,9]`), &sample)
        }
    }
    

    2000000 740 ns/op 224 B/op 4 allocs/op

    和 encoding/json 的区别是,标准库使用的是 reflect.ValueOf ,然后根据 json 的输入情况去找对应的 field 和 element 。而 jsoniter 的实现是反过来的,用 reflect.TypeOf 确定一个 json 的 schema ,然后根据 schema 产生对应的 decoder 。如果 json 输入不符合这个 decoder 则报错。

    StAX 风格的 API

    如果使用更底层的 api ,可以完全避免反射的开销

    func Benchmark_array_by_jsoniter_direct(b *testing.B) {
    	b.ReportAllocs()
    	for n := 0; n < b.N; n++ {
    		sample := make([]uint64, 0, 10)
    		iter := jsoniter.ParseString(`[1,2,3,4,5,6,7,8,9]`)
    		for iter.ReadArray() {
    			sample = append(sample, iter.ReadUint64())
    		}
    	}
    }
    

    3000000 455 ns/op 112 B/op 2 allocs/op

    9 条回复    2016-12-07 02:15:55 +08:00
    spider82
        1
    spider82  
       2016-12-05 00:53:39 +08:00
    mark
    scnace
        2
    scnace  
       2016-12-05 01:15:13 +08:00 via Android
    mark 一发起床看
    myself659410
        3
    myself659410  
       2016-12-05 09:44:33 +08:00
    https://github.com/pquerna/ffjson 也是 encoding/json 的 2 倍到 3 倍
    taowen
        4
    taowen  
    OP
       2016-12-05 10:05:17 +08:00
    @myself659410 ffjson 需要 go generate 静态生成代码,而 jsoniter 的性能数字是在同样使用反射的前提下测得的。另外 ffjson 不支持用 io.Reader 做为输入,需要把所有的 json 读入为 []byte 再处理。
    lwhile521
        5
    lwhile521  
       2016-12-05 10:16:04 +08:00
    已给 star~
    ggaaooppeenngg
        6
    ggaaooppeenngg  
       2016-12-05 10:57:24 +08:00
    用 reflect.TypeOf 确定一个 json 的 schema => 这个能认出 omitempty 这样的 tag 么?

    112 B/op 内存分配貌似降下来了, 为什么避开反射时间增加了?
    taowen
        7
    taowen  
    OP
       2016-12-05 11:34:27 +08:00
    @ggaaooppeenngg 支持了 -, string 这些 tag ,没有支持 omitempty 。

    ```
    type StructOfTag struct {
    field1 string `json:"field-1"`
    field2 string `json:"-"`
    field3 int `json:",string"`
    }

    func Test_reflect_struct_tag_field(t *testing.T) {
    err := jsoniter.Unmarshal(`{"field-1": "hello", "field2": "", "field3": "100"}`, &struct_)
    if struct_.field1 != "hello" {
    fmt.Println(err)
    t.Fatal(struct_.field1)
    }
    if struct_.field2 != "world" {
    fmt.Println(err)
    t.Fatal(struct_.field2)
    }
    if struct_.field3 != 100 {
    fmt.Println(err)
    t.Fatal(struct_.field3)
    }
    }
    ```

    不走反射速度是最快的啊 455 ns/op ,是标准库的 1/5
    ggaaooppeenngg
        8
    ggaaooppeenngg  
       2016-12-05 17:51:12 +08:00 via iPhone
    @taowen 哦哦,看错了,赞!

    在要求 json 完全相同的时候的确是不错的一个思路。
    nomoon
        9
    nomoon  
       2016-12-07 02:15:55 +08:00
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1004 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 21:13 · PVG 05:13 · LAX 13:13 · JFK 16:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.