我注意到,当我在 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
unmarshalDog()更有趣的是,当我运行同一个程序但在一个单独的 goroutine 中运行每次调用viago 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 的时间进行解组。所有直觉赞赏!
红颜莎娜
相关分类