Hi

I hope this is the right forum for posting this kind of question:
Debugging application performance, I came across JSON stream parsing 
causing a large number of mem allocations. Here's a snippet of code, which 
exemplifies my findings:

var (
 noSpace    = `{"Key":"StringValue"}`
 oneSpace   = `{"Key":"StringValue"} `
 twoSpace   = `{"Key":"StringValue" } `
 threeSpace = `{"Key": "StringValue" } `
 fourSpace  = `{"Key" : "StringValue" } `
 fiveSpace  = `{ "Key" : "StringValue" } `
)


func TryJSONStreamParsing(b *testing.B, jsonMsg string) {
 for i := 0; i < b.N; i++ {
 dec := json.NewDecoder(bytes.NewReader([]byte(jsonMsg)))
 t, err := dec.Token()
 if err != nil {
 b.Fatal(err)
 }
 if t != json.Delim('{') {
 b.Fatal("No {")
 }
 for dec.More() {
 t, err = dec.Token()
 if err != nil {
 b.Fatal(err)
 }
 if t == nil {
 b.Fatal("no t")
 }
 }
 }
}
func BenchmarkJSONNoSpace(b *testing.B) {
 TryJSONStreamParsing(b, noSpace)
}
func BenchmarkJSONOneSpace(b *testing.B) {
 TryJSONStreamParsing(b, oneSpace)
}
func BenchmarkJSONTwoSpace(b *testing.B) {
 TryJSONStreamParsing(b, twoSpace)
}
func BenchmarkJSONThreeSpace(b *testing.B) {
 TryJSONStreamParsing(b, threeSpace)
}
func BenchmarkJSONFourSpace(b *testing.B) {
 TryJSONStreamParsing(b, fourSpace)
}
func BenchmarkJSONFiveSpace(b *testing.B) {
 TryJSONStreamParsing(b, fiveSpace)
}



Here's an example output for running that:
BenchmarkJSONNoSpace-4                    500000      3166 ns/op    1304 
B/op      24 allocs/op
BenchmarkJSONOneSpace-4                  500000      2995 ns/op    1304 B/op   
   24 allocs/op
BenchmarkJSONTwoSpace-4                  500000      2423 ns/op    1200 B/op   
   18 allocs/op
BenchmarkJSONThreeSpace-4               1000000      2533 ns/op    1200 B/op   
   18 allocs/op
BenchmarkJSONFourSpace-4                 1000000      2145 ns/op    1104 
B/op      12 allocs/op
BenchmarkJSONFiveSpace-4                 1000000      2069 ns/op    1104 
B/op      12 allocs/op

Depending on where the whitespace is put in the JSON message, there are 
very different count of memory allocs.

When comparing memory profiles of the worst case vs. the best case...
Top 7 items in worst case:
   2255615 27.06% 27.06%    3074826 36.89%  encoding/json.(*scanner).error
   2064414 24.77% 51.83%    6761536 81.12%  encoding/json.(*Decoder).Token
   1573541 18.88% 70.71%    8335077   100%  .....TryJSONStreamParsing
   1081360 12.97% 83.68%    1081360 12.97% 
 encoding/json.(*decodeState).literalStore
    819211  9.83% 93.51%     819211  9.83%  strconv.quoteWith
    540936  6.49%   100%     540936  6.49%  encoding/json.(*Decoder).refill
         0     0%   100%    4156186 49.86%  encoding/json.(*Decoder).Decode

Top 7 items in best case:
   3932219 41.93% 41.93%    6762044 72.10%  encoding/json.(*Decoder).Token
   2616008 27.89% 69.82%    9378052   100%  .....TryJSONStreamParsing
   1835036 19.57% 89.39%    1835036 19.57% 
 encoding/json.(*decodeState).literalStore
    994789 10.61%   100%     994789 10.61%  encoding/json.(*Decoder).refill
         0     0%   100%    1835036 19.57%  encoding/json.(*Decoder).Decode
         0     0%   100%     994789 10.61%  encoding/json.(*Decoder).peek


... it seems that the more compact JSON causes the Decoder to generate 
errors (which are not surfaced and which causes allocations). While the 
JSON with some extra spaces causes a cleaner parse.

Does anyone know why the JSON parser consider missing whitespace after 
values for and error?
Or am I using the json.Decoder incorrectly?

Cheers
Tonny

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to