我有一项服务需要编组大型 JSON(长度约为 50KB)。 一开始,我使用map[string]interface{}并对其进行编码,但它消耗了大量的CPU。然后我尝试将 JSON 映射到结构并对结构进行编码,但它没有解决问题。 我尝试了 ffjson、easyjson 但它们没有改善情况。 你知道我可以做什么优化吗?
过早的优化是万恶之源。
唐纳德·高德纳
显然,您只描述了解码,当然解码将占据解码过程的重要部分。
因此出现了 JSON 解组是否客观有效的问题。
我运行了一些基准测试:
package gounmarshalperformance78772815
import (
"encoding/json"
"os"
"testing"
"github.com/stretchr/testify/require"
)
type unknownJson map[string]interface{}
func benchmark(path string, arraysize int, b *testing.B) {
d, err := os.ReadFile(path)
require.NoError(b, err)
for i := 0; i < b.N; i++ {
foo := make([]unknownJson, arraysize)
err := json.Unmarshal(d, &foo)
require.NoError(b, err)
}
}
func Benchmark64k(b *testing.B) {
// Array sizes were determined by running the following command:
// $ jq length testdata/78772815-{64k,128k,256k}.json
benchmark("testdata/78772815-64k.json", 197, b)
}
func Benchmark128k(b *testing.B) {
benchmark("testdata/78772815-128k.json", 788, b)
}
func Benchmark256k(b *testing.B) {
benchmark("testdata/78772815-256k.json", 792, b)
}
结果如下:
$ go test -bench=. -cpu=1,2,4,6,8,10
goos: darwin
goarch: arm64
pkg: github.com/mwmahlberg/go-unmarshalperformance-78772815
Benchmark64k 2821 423092 ns/op
Benchmark64k-2 2966 389132 ns/op
Benchmark64k-4 2904 394141 ns/op
Benchmark64k-6 2972 398771 ns/op
Benchmark64k-8 2931 396255 ns/op
Benchmark64k-10 2901 392617 ns/op
Benchmark128k 660 1823380 ns/op
Benchmark128k-2 704 1691047 ns/op
Benchmark128k-4 697 1731524 ns/op
Benchmark128k-6 691 1832979 ns/op
Benchmark128k-8 705 1705955 ns/op
Benchmark128k-10 698 1714762 ns/op
Benchmark256k 658 1842479 ns/op
Benchmark256k-2 690 1705684 ns/op
Benchmark256k-4 687 1747386 ns/op
Benchmark256k-6 680 1738418 ns/op
Benchmark256k-8 696 1763082 ns/op
Benchmark256k-10 687 1760691 ns/op
PASS
ok github.com/mwmahlberg/go-unmarshalperformance-78772815 25.038s
那么,这告诉我们什么?它告诉我们,大小为 64k 的测试数据 JSON 每秒至少被解组 2821 次。
如果这还不够性能,您应该考虑水平缩放。