json - 为什么在 "warming up"之后解码 JSON 更快?

标签 json performance go optimization

我注意到,当我在 Go HTTP 服务器的上下文中计时 JSON 解码时,即使是小对象也需要 30,000+ 纳秒。这对我来说似乎很大,所以我运行了一些独立的基准测试,结果令人惊讶地显示每次解码的平均时间约为 500 纳米。为了更深入地研究这个问题,我编写了一个程序,它只对同一个小对象执行一系列解码,结果表明第一个解码很慢,而后续的要快得多:

package main

import (
    "time"
    "fmt"
    "encoding/json"
)

var b []byte

type Dog struct {
    Age int `json:"Age"`
}

func unmarshalDog() {
    dogCopy := Dog{}

    start := time.Now().UnixNano()
    json.Unmarshal(b, &dogCopy)
    end := time.Now().UnixNano()
    fmt.Printf("Time to marshal/unmarshal: %d\n", end-start)
}

func main() {
    // Marshal an object into a byte array which we will repeatedly unmarshal from
    d := Dog {
        Age: 5,
    }

    var err error
    b, err = json.Marshal(d)
    if err != nil {
        panic(err)
    }

    for i := 0; i < 20; i++ {
        unmarshalDog()
    }

    // Allow the goroutines to finish before terminating execution.
    time.Sleep(3 * time.Second)
}

输出:

$ go run testJSONPerformance.go
Time to marshal/unmarshal: 34127
Time to marshal/unmarshal: 1465
Time to marshal/unmarshal: 979
Time to marshal/unmarshal: 892
Time to marshal/unmarshal: 849
Time to marshal/unmarshal: 814
Time to marshal/unmarshal: 822
Time to marshal/unmarshal: 822
Time to marshal/unmarshal: 815
Time to marshal/unmarshal: 829
Time to marshal/unmarshal: 822
Time to marshal/unmarshal: 819

更有趣的是,当我运行同一个程序但通过 go unmarshalDog() 在单独的 goroutine 中运行对 unmarshalDog() 的每次调用时,预热现象消失了并且平均解码时间要长得多:

Time to marshal/unmarshal: 36540
Time to marshal/unmarshal: 4652
Time to marshal/unmarshal: 56959
Time to marshal/unmarshal: 3887
Time to marshal/unmarshal: 57068
Time to marshal/unmarshal: 3519
Time to marshal/unmarshal: 37160

我还尝试了一个版本,而不是解码相同的字节数组,我每次都编码和解码一个不同的对象(以防某种运行时缓存正在进行)。在这种情况下,结果是一样的。

我非常想知道这种明显的“热身”是怎么回事。在 HTTP 服务器的上下文中,每个请求都会得到一个不同的 goroutine,因此每个解码平均来说都非常慢。这似乎不是最理想的,因为显然 Go 有可能在特定上下文中用 1/50 的时间进行解码。感谢所有直觉!

最佳答案

JSON 解码器缓存有关任何类型的第一次解码的信息。该类型的后续解码使用此缓存信息。

缓存在 goroutine 之间共享,但是没有代码来确保只有一个 goroutine 尝试创建第一个缓存值。一旦一个 goroutine 将一个值存储到缓存中,所有新来者都将使用该缓存值。

缓存的代码是here .

关于json - 为什么在 "warming up"之后解码 JSON 更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51990634/

相关文章:

python - 加速 numpy 小函数

mysql - JOIN 或 LEFT JOIN 是否持续检查 SELECT 查询?

r - 将重复值合二为一后连接两列的更好/更快的方法?

hash - Go:这个散列函数的范围如何从 0-32 位?

c# - AWS Hello World C# Lambda 函数返回 Json 错误

javascript - AJAX get 请求将每个字母放入数组中

javascript - Paypal 快速结账 : Successful transaction but cannot show the order detail in seller account

java - 如何正确获取 WebTarget 获取请求响应 (JAX-RS) 的正文?

Go:学习 channel &排队, fatal error

go - 如何检测当前 Go 进程是否在 headless (headless)(非 GUI)环境中运行?