Golangフロー解析Json
json-iteratorライブラリ:
既存のgolang解析jsonのライブラリはpushモードであり,pull apiベースのライブラリが欠けている.またgolang解析jsonの速度がどのようになっているのか、どれだけの向上空間があるのかを見てみましょう.
APIスタイル
apiスタイルはStAXをベースにしていますが、JSON向けに特別に最適化されています.StAXやSAXよりも簡単にコントロールできます.もちろん最も簡単であれば、DOMクラスのapiが最も簡単です.フローpullを用いたapiは制御解析過程を最大化することである.
解析Array
pull apiのスタイルが非常に異なることがわかります.解析プロセス全体が呼び出し元によって駆動されます
解析Object
解析プロセスは反射に依存せず、解析された値が何をするかは完全にあなたが操作します.値をオブジェクトにバインドすることなく、直接累積操作を行うことができます.
SKIP
関心のないフィールドについては、スキップを選択できます.
パフォーマンスの最適化
このプロジェクトのもう一つの目的はgolang原生のjson apiが速いか遅いか、空間を高めるかどうかを見ることです.
ストリーム解析に基づいて、メモリに一度に読み込む必要はありません.
5 215547514 ns/op 71467118 B/op 272476 allocs/op
10 110209750 ns/op 4248 B/op 5 allocs/op
json iteratorの実装はメモリの消費量を非常に節約していることがわかります.標準ライブラリの実装より2倍速い.GCの圧力はもっと小さくなった.
直接intを解析する
intの解析には2回必要なく,一度に読み取る.ParseIntの実装をjson解析のコードに統合する.
10000000 189 ns/op
1000000 1327 ns/op
このシーンは7 xのスピードです
反射なし、schemaの解析あり
schema解析に従ってif-else判定を減らす.反射なしで直接値を割り当てる
2000000 640 ns/op
1000000 1816 ns/op
まとめ
golangが持参したjsonライブラリは実は性能がいいです.benchmark(https://github.com/json-itera...)他のストリームベースの解析ライブラリよりも高速(https://github.com/ugorji/go/...).このライブラリはhttps://github.com/pquerna/ff...高速と呼ばれていますが、フロー解析はサポートされていません(すべての[]byteをメモリに事前に読み込む必要があります).ほとんどの場合、golangが持っているもので十分です.他のjson解析ライブラリをむやみに整理しないでください.
pull apiが必要な場合、または追加の2 x~6 x性能が必要な場合、
https://github.com/json-iterator/go
動機既存のgolang解析jsonのライブラリはpushモードであり,pull apiベースのライブラリが欠けている.またgolang解析jsonの速度がどのようになっているのか、どれだけの向上空間があるのかを見てみましょう.
APIスタイル
apiスタイルはStAXをベースにしていますが、JSON向けに特別に最適化されています.StAXやSAXよりも簡単にコントロールできます.もちろん最も簡単であれば、DOMクラスのapiが最も簡単です.フローpullを用いたapiは制御解析過程を最大化することである.
解析Array
iter := ParseString(`[1,2,3]`)
for iter.ReadArray() {
iter.ReadUint64()
}
pull apiのスタイルが非常に異なることがわかります.解析プロセス全体が呼び出し元によって駆動されます
解析Object
type TestObj struct {
Field1 string
Field2 uint64
}
iter := ParseString(`{"field1": "1", "field2": 2}`)
obj := TestObj{}
for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
switch field {
case "field1":
obj.Field1 = iter.ReadString()
case "field2":
obj.Field2 = iter.ReadUint64()
default:
iter.ReportError("bind object", "unexpected field")
}
}
解析プロセスは反射に依存せず、解析された値が何をするかは完全にあなたが操作します.値をオブジェクトにバインドすることなく、直接累積操作を行うことができます.
SKIP
iter := ParseString(`[ {"a" : [{"b": "c"}], "d": 102 }, "b"]`)
iter.ReadArray()
iter.Skip()
iter.ReadArray()
if iter.ReadString() != "b" {
t.FailNow()
}
関心のないフィールドについては、スキップを選択できます.
パフォーマンスの最適化
このプロジェクトのもう一つの目的はgolang原生のjson apiが速いか遅いか、空間を高めるかどうかを見ることです.
ストリーム解析に基づいて、メモリに一度に読み込む必要はありません.
// "encoding/json"
func Benchmark_stardard_lib(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
file, _ := os.Open("/tmp/large-file.json")
result := []struct{}{}
decoder := json.NewDecoder(file)
decoder.Decode(&result)
file.Close()
}
}
5 215547514 ns/op 71467118 B/op 272476 allocs/op
// "github.com/json-iterator/go"
func Benchmark_jsoniter(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
file, _ := os.Open("/tmp/large-file.json")
iter := jsoniter.Parse(file, 1024)
for iter.ReadArray() {
iter.Skip()
}
file.Close()
}
}
10 110209750 ns/op 4248 B/op 5 allocs/op
json iteratorの実装はメモリの消費量を非常に節約していることがわかります.標準ライブラリの実装より2倍速い.GCの圧力はもっと小さくなった.
直接intを解析する
intの解析には2回必要なく,一度に読み取る.ParseIntの実装をjson解析のコードに統合する.
func Benchmark_jsoniter_array(b *testing.B) {
for n := 0; n < b.N; n++ {
iter := ParseString(`[1,2,3]`)
for iter.ReadArray() {
iter.ReadUint64()
}
}
}
10000000 189 ns/op
func Benchmark_json_array(b *testing.B) {
for n := 0; n < b.N; n++ {
result := []interface{}{}
json.Unmarshal([]byte(`[1,2,3]`), &result)
}
}
1000000 1327 ns/op
このシーンは7 xのスピードです
反射なし、schemaの解析あり
schema解析に従ってif-else判定を減らす.反射なしで直接値を割り当てる
type Level1 struct {
Hello []Level2
}
type Level2 struct {
World string
}
func Benchmark_jsoniter_nested(b *testing.B) {
for n := 0; n < b.N; n++ {
iter := ParseString(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`)
l1 := Level1{}
for l1Field := iter.ReadObject(); l1Field != ""; l1Field = iter.ReadObject() {
switch l1Field {
case "hello":
l1.Hello = readLevel1Hello(iter)
default:
iter.Skip()
}
}
}
}
func readLevel1Hello(iter *Iterator) []Level2 {
l2Array := make([]Level2, 0, 2)
for iter.ReadArray() {
l2 := Level2{}
for l2Field := iter.ReadObject(); l2Field != ""; l2Field = iter.ReadObject() {
switch l2Field {
case "world":
l2.World = iter.ReadString()
default:
iter.Skip()
}
}
l2Array = append(l2Array, l2)
}
return l2Array
}
2000000 640 ns/op
func Benchmark_json_nested(b *testing.B) {
for n := 0; n < b.N; n++ {
l1 := Level1{}
json.Unmarshal([]byte(`{"hello": [{"world": "value1"}, {"world": "value2"}]}`), &l1)
}
}
1000000 1816 ns/op
まとめ
golangが持参したjsonライブラリは実は性能がいいです.benchmark(https://github.com/json-itera...)他のストリームベースの解析ライブラリよりも高速(https://github.com/ugorji/go/...).このライブラリはhttps://github.com/pquerna/ff...高速と呼ばれていますが、フロー解析はサポートされていません(すべての[]byteをメモリに事前に読み込む必要があります).ほとんどの場合、golangが持っているもので十分です.他のjson解析ライブラリをむやみに整理しないでください.
pull apiが必要な場合、または追加の2 x~6 x性能が必要な場合、
https://github.com/json-iterator/go
が考えられる.