gitee.com/quant1x/gox@v1.7.6/fastjson/parser_timing_test.go (about)

     1  package fastjson
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"strings"
     8  	"testing"
     9  )
    10  
    11  func BenchmarkParseRawString(b *testing.B) {
    12  	for _, s := range []string{`""`, `"a"`, `"abcd"`, `"abcdefghijk"`, `"qwertyuiopasdfghjklzxcvb"`} {
    13  		b.Run(s, func(b *testing.B) {
    14  			benchmarkParseRawString(b, s)
    15  		})
    16  	}
    17  }
    18  
    19  func benchmarkParseRawString(b *testing.B, s string) {
    20  	b.ReportAllocs()
    21  	b.SetBytes(int64(len(s)))
    22  	s = s[1:] // skip the opening '"'
    23  	b.RunParallel(func(pb *testing.PB) {
    24  		for pb.Next() {
    25  			rs, tail, err := parseRawString(s)
    26  			if err != nil {
    27  				panic(fmt.Errorf("cannot parse %q: %s", s, err))
    28  			}
    29  			if rs != s[:len(s)-1] {
    30  				panic(fmt.Errorf("invalid string obtained; got %q; want %q", rs, s[:len(s)-1]))
    31  			}
    32  			if len(tail) > 0 {
    33  				panic(fmt.Errorf("non-empty tail got: %q", tail))
    34  			}
    35  		}
    36  	})
    37  }
    38  
    39  func BenchmarkParseRawNumber(b *testing.B) {
    40  	for _, s := range []string{"1", "1234", "123456", "-1234", "1234567890.1234567", "-1.32434e+12"} {
    41  		b.Run(s, func(b *testing.B) {
    42  			benchmarkParseRawNumber(b, s)
    43  		})
    44  	}
    45  }
    46  
    47  func benchmarkParseRawNumber(b *testing.B, s string) {
    48  	b.ReportAllocs()
    49  	b.SetBytes(int64(len(s)))
    50  	b.RunParallel(func(pb *testing.PB) {
    51  		for pb.Next() {
    52  			rn, tail, err := parseRawNumber(s)
    53  			if err != nil {
    54  				panic(fmt.Errorf("cannot parse %q: %s", s, err))
    55  			}
    56  			if rn != s {
    57  				panic(fmt.Errorf("invalid number obtained; got %q; want %q", rn, s))
    58  			}
    59  			if len(tail) > 0 {
    60  				panic(fmt.Errorf("non-empty tail got: %q", tail))
    61  			}
    62  		}
    63  	})
    64  }
    65  
    66  func BenchmarkObjectGet(b *testing.B) {
    67  	for _, itemsCount := range []int{10, 100, 1000, 10000, 100000} {
    68  		b.Run(fmt.Sprintf("items_%d", itemsCount), func(b *testing.B) {
    69  			for _, lookupsCount := range []int{0, 1, 2, 4, 8, 16, 32, 64} {
    70  				b.Run(fmt.Sprintf("lookups_%d", lookupsCount), func(b *testing.B) {
    71  					benchmarkObjectGet(b, itemsCount, lookupsCount)
    72  				})
    73  			}
    74  		})
    75  	}
    76  }
    77  
    78  func benchmarkObjectGet(b *testing.B, itemsCount, lookupsCount int) {
    79  	b.StopTimer()
    80  	var ss []string
    81  	for i := 0; i < itemsCount; i++ {
    82  		s := fmt.Sprintf(`"key_%d": "value_%d"`, i, i)
    83  		ss = append(ss, s)
    84  	}
    85  	s := "{" + strings.Join(ss, ",") + "}"
    86  	key := fmt.Sprintf("key_%d", len(ss)/2)
    87  	expectedValue := fmt.Sprintf("value_%d", len(ss)/2)
    88  	b.StartTimer()
    89  	b.ReportAllocs()
    90  	b.SetBytes(int64(len(s)))
    91  
    92  	b.RunParallel(func(pb *testing.PB) {
    93  		p := benchPool.Get()
    94  		for pb.Next() {
    95  			v, err := p.Parse(s)
    96  			if err != nil {
    97  				panic(fmt.Errorf("unexpected error: %s", err))
    98  			}
    99  			o := v.GetObject()
   100  			for i := 0; i < lookupsCount; i++ {
   101  				sb := o.Get(key).GetStringBytes()
   102  				if string(sb) != expectedValue {
   103  					panic(fmt.Errorf("unexpected value; got %q; want %q", sb, expectedValue))
   104  				}
   105  			}
   106  		}
   107  		benchPool.Put(p)
   108  	})
   109  }
   110  
   111  func BenchmarkMarshalTo(b *testing.B) {
   112  	b.Run("small", func(b *testing.B) {
   113  		benchmarkMarshalTo(b, smallFixture)
   114  	})
   115  	b.Run("medium", func(b *testing.B) {
   116  		benchmarkMarshalTo(b, mediumFixture)
   117  	})
   118  	b.Run("large", func(b *testing.B) {
   119  		benchmarkMarshalTo(b, largeFixture)
   120  	})
   121  	b.Run("canada", func(b *testing.B) {
   122  		benchmarkMarshalTo(b, canadaFixture)
   123  	})
   124  	b.Run("citm", func(b *testing.B) {
   125  		benchmarkMarshalTo(b, citmFixture)
   126  	})
   127  	b.Run("twitter", func(b *testing.B) {
   128  		benchmarkMarshalTo(b, twitterFixture)
   129  	})
   130  }
   131  
   132  func benchmarkMarshalTo(b *testing.B, s string) {
   133  	p := benchPool.Get()
   134  	v, err := p.Parse(s)
   135  	if err != nil {
   136  		panic(fmt.Errorf("unexpected error: %s", err))
   137  	}
   138  
   139  	b.ReportAllocs()
   140  	b.SetBytes(int64(len(s)))
   141  	b.RunParallel(func(pb *testing.PB) {
   142  		var b []byte
   143  		for pb.Next() {
   144  			// It is ok calling v.MarshalTo from concurrent
   145  			// goroutines, since MarshalTo doesn't modify v.
   146  			b = v.MarshalTo(b[:0])
   147  		}
   148  	})
   149  	benchPool.Put(p)
   150  }
   151  
   152  func BenchmarkParse(b *testing.B) {
   153  	b.Run("small", func(b *testing.B) {
   154  		benchmarkParse(b, smallFixture)
   155  	})
   156  	b.Run("medium", func(b *testing.B) {
   157  		benchmarkParse(b, mediumFixture)
   158  	})
   159  	b.Run("large", func(b *testing.B) {
   160  		benchmarkParse(b, largeFixture)
   161  	})
   162  	b.Run("canada", func(b *testing.B) {
   163  		benchmarkParse(b, canadaFixture)
   164  	})
   165  	b.Run("citm", func(b *testing.B) {
   166  		benchmarkParse(b, citmFixture)
   167  	})
   168  	b.Run("twitter", func(b *testing.B) {
   169  		benchmarkParse(b, twitterFixture)
   170  	})
   171  }
   172  
   173  var (
   174  	// small, medium and large fixtures are from https://github.com/buger/jsonparser/blob/f04e003e4115787c6272636780bc206e5ffad6c4/benchmark/benchmark.go
   175  	smallFixture  = getFromFile("testdata/small.json")
   176  	mediumFixture = getFromFile("testdata/medium.json")
   177  	largeFixture  = getFromFile("testdata/large.json")
   178  
   179  	// canada, citm and twitter fixtures are from https://github.com/serde-rs/json-benchmark/tree/0db02e043b3ae87dc5065e7acb8654c1f7670c43/data
   180  	canadaFixture  = getFromFile("testdata/canada.json")
   181  	citmFixture    = getFromFile("testdata/citm_catalog.json")
   182  	twitterFixture = getFromFile("testdata/twitter.json")
   183  )
   184  
   185  func getFromFile(filename string) string {
   186  	data, err := ioutil.ReadFile(filename)
   187  	if err != nil {
   188  		panic(fmt.Errorf("cannot read %s: %s", filename, err))
   189  	}
   190  	return string(data)
   191  }
   192  
   193  func benchmarkParse(b *testing.B, s string) {
   194  	b.Run("stdjson-map", func(b *testing.B) {
   195  		benchmarkStdJSONParseMap(b, s)
   196  	})
   197  	b.Run("stdjson-struct", func(b *testing.B) {
   198  		benchmarkStdJSONParseStruct(b, s)
   199  	})
   200  	b.Run("stdjson-empty-struct", func(b *testing.B) {
   201  		benchmarkStdJSONParseEmptyStruct(b, s)
   202  	})
   203  	b.Run("fastjson", func(b *testing.B) {
   204  		benchmarkFastJSONParse(b, s)
   205  	})
   206  	b.Run("fastjson-get", func(b *testing.B) {
   207  		benchmarkFastJSONParseGet(b, s)
   208  	})
   209  }
   210  
   211  func benchmarkFastJSONParse(b *testing.B, s string) {
   212  	b.ReportAllocs()
   213  	b.SetBytes(int64(len(s)))
   214  	b.RunParallel(func(pb *testing.PB) {
   215  		p := benchPool.Get()
   216  		for pb.Next() {
   217  			v, err := p.Parse(s)
   218  			if err != nil {
   219  				panic(fmt.Errorf("unexpected error: %s", err))
   220  			}
   221  			if v.Type() != TypeObject {
   222  				panic(fmt.Errorf("unexpected value type; got %s; want %s", v.Type(), TypeObject))
   223  			}
   224  		}
   225  		benchPool.Put(p)
   226  	})
   227  }
   228  
   229  func benchmarkFastJSONParseGet(b *testing.B, s string) {
   230  	b.ReportAllocs()
   231  	b.SetBytes(int64(len(s)))
   232  	b.RunParallel(func(pb *testing.PB) {
   233  		p := benchPool.Get()
   234  		var n int
   235  		for pb.Next() {
   236  			v, err := p.Parse(s)
   237  			if err != nil {
   238  				panic(fmt.Errorf("unexpected error: %s", err))
   239  			}
   240  			n += v.GetInt("sid")
   241  			n += len(v.GetStringBytes("uuid"))
   242  			p := v.Get("person")
   243  			if p != nil {
   244  				n++
   245  			}
   246  			c := v.Get("company")
   247  			if c != nil {
   248  				n++
   249  			}
   250  			u := v.Get("users")
   251  			if u != nil {
   252  				n++
   253  			}
   254  			a := v.GetArray("features")
   255  			n += len(a)
   256  			a = v.GetArray("topicSubTopics")
   257  			n += len(a)
   258  			o := v.Get("search_metadata")
   259  			if o != nil {
   260  				n++
   261  			}
   262  		}
   263  		benchPool.Put(p)
   264  	})
   265  }
   266  
   267  var benchPool ParserPool
   268  
   269  func benchmarkStdJSONParseMap(b *testing.B, s string) {
   270  	b.ReportAllocs()
   271  	b.SetBytes(int64(len(s)))
   272  	bb := s2b(s)
   273  	b.RunParallel(func(pb *testing.PB) {
   274  		var m map[string]interface{}
   275  		for pb.Next() {
   276  			if err := json.Unmarshal(bb, &m); err != nil {
   277  				panic(fmt.Errorf("unexpected error: %s", err))
   278  			}
   279  		}
   280  	})
   281  }
   282  
   283  func benchmarkStdJSONParseStruct(b *testing.B, s string) {
   284  	b.ReportAllocs()
   285  	b.SetBytes(int64(len(s)))
   286  	bb := s2b(s)
   287  	b.RunParallel(func(pb *testing.PB) {
   288  		var m struct {
   289  			Sid            int
   290  			UUID           string
   291  			Person         map[string]interface{}
   292  			Company        map[string]interface{}
   293  			Users          []interface{}
   294  			Features       []map[string]interface{}
   295  			TopicSubTopics map[string]interface{}
   296  			SearchMetadata map[string]interface{}
   297  		}
   298  		for pb.Next() {
   299  			if err := json.Unmarshal(bb, &m); err != nil {
   300  				panic(fmt.Errorf("unexpected error: %s", err))
   301  			}
   302  		}
   303  	})
   304  }
   305  
   306  func benchmarkStdJSONParseEmptyStruct(b *testing.B, s string) {
   307  	b.ReportAllocs()
   308  	b.SetBytes(int64(len(s)))
   309  	bb := s2b(s)
   310  	b.RunParallel(func(pb *testing.PB) {
   311  		var m struct{}
   312  		for pb.Next() {
   313  			if err := json.Unmarshal(bb, &m); err != nil {
   314  				panic(fmt.Errorf("unexpected error: %s", err))
   315  			}
   316  		}
   317  	})
   318  }