Source file src/encoding/json/v2_bench_test.go

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build goexperiment.jsonv2
     6  
     7  // Large data benchmark.
     8  // The JSON data is a summary of agl's changes in the
     9  // go, webkit, and chromium open source projects.
    10  // We benchmark converting between the JSON form
    11  // and in-memory data structures.
    12  
    13  package json
    14  
    15  import (
    16  	"bytes"
    17  	"crypto/sha256"
    18  	"fmt"
    19  	"io"
    20  	"strings"
    21  	"testing"
    22  
    23  	"encoding/json/internal/jsontest"
    24  )
    25  
    26  type codeResponse struct {
    27  	Tree     *codeNode `json:"tree"`
    28  	Username string    `json:"username"`
    29  }
    30  
    31  type codeNode struct {
    32  	Name     string      `json:"name"`
    33  	Kids     []*codeNode `json:"kids"`
    34  	CLWeight float64     `json:"cl_weight"`
    35  	Touches  int         `json:"touches"`
    36  	MinT     int64       `json:"min_t"`
    37  	MaxT     int64       `json:"max_t"`
    38  	MeanT    int64       `json:"mean_t"`
    39  }
    40  
    41  var codeJSON []byte
    42  var codeStruct codeResponse
    43  
    44  func codeInit() {
    45  	var data []byte
    46  	for _, entry := range jsontest.Data {
    47  		if entry.Name == "GolangSource" {
    48  			data = entry.Data()
    49  		}
    50  	}
    51  	codeJSON = data
    52  
    53  	if err := Unmarshal(codeJSON, &codeStruct); err != nil {
    54  		panic("unmarshal code.json: " + err.Error())
    55  	}
    56  
    57  	var err error
    58  	if data, err = Marshal(&codeStruct); err != nil {
    59  		panic("marshal code.json: " + err.Error())
    60  	}
    61  
    62  	if !bytes.Equal(data, codeJSON) {
    63  		println("different lengths", len(data), len(codeJSON))
    64  		for i := 0; i < len(data) && i < len(codeJSON); i++ {
    65  			if data[i] != codeJSON[i] {
    66  				println("re-marshal: changed at byte", i)
    67  				println("orig: ", string(codeJSON[i-10:i+10]))
    68  				println("new: ", string(data[i-10:i+10]))
    69  				break
    70  			}
    71  		}
    72  		panic("re-marshal code.json: different result")
    73  	}
    74  }
    75  
    76  func BenchmarkCodeEncoder(b *testing.B) {
    77  	b.ReportAllocs()
    78  	if codeJSON == nil {
    79  		b.StopTimer()
    80  		codeInit()
    81  		b.StartTimer()
    82  	}
    83  	b.RunParallel(func(pb *testing.PB) {
    84  		enc := NewEncoder(io.Discard)
    85  		for pb.Next() {
    86  			if err := enc.Encode(&codeStruct); err != nil {
    87  				b.Fatalf("Encode error: %v", err)
    88  			}
    89  		}
    90  	})
    91  	b.SetBytes(int64(len(codeJSON)))
    92  }
    93  
    94  func BenchmarkCodeEncoderError(b *testing.B) {
    95  	b.ReportAllocs()
    96  	if codeJSON == nil {
    97  		b.StopTimer()
    98  		codeInit()
    99  		b.StartTimer()
   100  	}
   101  
   102  	// Trigger an error in Marshal with cyclic data.
   103  	type Dummy struct {
   104  		Name string
   105  		Next *Dummy
   106  	}
   107  	dummy := Dummy{Name: "Dummy"}
   108  	dummy.Next = &dummy
   109  
   110  	b.RunParallel(func(pb *testing.PB) {
   111  		enc := NewEncoder(io.Discard)
   112  		for pb.Next() {
   113  			if err := enc.Encode(&codeStruct); err != nil {
   114  				b.Fatalf("Encode error: %v", err)
   115  			}
   116  			if _, err := Marshal(dummy); err == nil {
   117  				b.Fatal("Marshal error: got nil, want non-nil")
   118  			}
   119  		}
   120  	})
   121  	b.SetBytes(int64(len(codeJSON)))
   122  }
   123  
   124  func BenchmarkCodeMarshal(b *testing.B) {
   125  	b.ReportAllocs()
   126  	if codeJSON == nil {
   127  		b.StopTimer()
   128  		codeInit()
   129  		b.StartTimer()
   130  	}
   131  	b.RunParallel(func(pb *testing.PB) {
   132  		for pb.Next() {
   133  			if _, err := Marshal(&codeStruct); err != nil {
   134  				b.Fatalf("Marshal error: %v", err)
   135  			}
   136  		}
   137  	})
   138  	b.SetBytes(int64(len(codeJSON)))
   139  }
   140  
   141  func BenchmarkCodeMarshalError(b *testing.B) {
   142  	b.ReportAllocs()
   143  	if codeJSON == nil {
   144  		b.StopTimer()
   145  		codeInit()
   146  		b.StartTimer()
   147  	}
   148  
   149  	// Trigger an error in Marshal with cyclic data.
   150  	type Dummy struct {
   151  		Name string
   152  		Next *Dummy
   153  	}
   154  	dummy := Dummy{Name: "Dummy"}
   155  	dummy.Next = &dummy
   156  
   157  	b.RunParallel(func(pb *testing.PB) {
   158  		for pb.Next() {
   159  			if _, err := Marshal(&codeStruct); err != nil {
   160  				b.Fatalf("Marshal error: %v", err)
   161  			}
   162  			if _, err := Marshal(dummy); err == nil {
   163  				b.Fatal("Marshal error: got nil, want non-nil")
   164  			}
   165  		}
   166  	})
   167  	b.SetBytes(int64(len(codeJSON)))
   168  }
   169  
   170  func benchMarshalBytes(n int) func(*testing.B) {
   171  	sample := []byte("hello world")
   172  	// Use a struct pointer, to avoid an allocation when passing it as an
   173  	// interface parameter to Marshal.
   174  	v := &struct {
   175  		Bytes []byte
   176  	}{
   177  		bytes.Repeat(sample, (n/len(sample))+1)[:n],
   178  	}
   179  	return func(b *testing.B) {
   180  		for i := 0; i < b.N; i++ {
   181  			if _, err := Marshal(v); err != nil {
   182  				b.Fatalf("Marshal error: %v", err)
   183  			}
   184  		}
   185  	}
   186  }
   187  
   188  func benchMarshalBytesError(n int) func(*testing.B) {
   189  	sample := []byte("hello world")
   190  	// Use a struct pointer, to avoid an allocation when passing it as an
   191  	// interface parameter to Marshal.
   192  	v := &struct {
   193  		Bytes []byte
   194  	}{
   195  		bytes.Repeat(sample, (n/len(sample))+1)[:n],
   196  	}
   197  
   198  	// Trigger an error in Marshal with cyclic data.
   199  	type Dummy struct {
   200  		Name string
   201  		Next *Dummy
   202  	}
   203  	dummy := Dummy{Name: "Dummy"}
   204  	dummy.Next = &dummy
   205  
   206  	return func(b *testing.B) {
   207  		for i := 0; i < b.N; i++ {
   208  			if _, err := Marshal(v); err != nil {
   209  				b.Fatalf("Marshal error: %v", err)
   210  			}
   211  			if _, err := Marshal(dummy); err == nil {
   212  				b.Fatal("Marshal error: got nil, want non-nil")
   213  			}
   214  		}
   215  	}
   216  }
   217  
   218  func BenchmarkMarshalBytes(b *testing.B) {
   219  	b.ReportAllocs()
   220  	// 32 fits within encodeState.scratch.
   221  	b.Run("32", benchMarshalBytes(32))
   222  	// 256 doesn't fit in encodeState.scratch, but is small enough to
   223  	// allocate and avoid the slower base64.NewEncoder.
   224  	b.Run("256", benchMarshalBytes(256))
   225  	// 4096 is large enough that we want to avoid allocating for it.
   226  	b.Run("4096", benchMarshalBytes(4096))
   227  }
   228  
   229  func BenchmarkMarshalBytesError(b *testing.B) {
   230  	b.ReportAllocs()
   231  	// 32 fits within encodeState.scratch.
   232  	b.Run("32", benchMarshalBytesError(32))
   233  	// 256 doesn't fit in encodeState.scratch, but is small enough to
   234  	// allocate and avoid the slower base64.NewEncoder.
   235  	b.Run("256", benchMarshalBytesError(256))
   236  	// 4096 is large enough that we want to avoid allocating for it.
   237  	b.Run("4096", benchMarshalBytesError(4096))
   238  }
   239  
   240  func BenchmarkMarshalMap(b *testing.B) {
   241  	b.ReportAllocs()
   242  	m := map[string]int{
   243  		"key3": 3,
   244  		"key2": 2,
   245  		"key1": 1,
   246  	}
   247  	b.RunParallel(func(pb *testing.PB) {
   248  		for pb.Next() {
   249  			if _, err := Marshal(m); err != nil {
   250  				b.Fatal("Marshal:", err)
   251  			}
   252  		}
   253  	})
   254  }
   255  
   256  func BenchmarkCodeDecoder(b *testing.B) {
   257  	b.ReportAllocs()
   258  	if codeJSON == nil {
   259  		b.StopTimer()
   260  		codeInit()
   261  		b.StartTimer()
   262  	}
   263  	b.RunParallel(func(pb *testing.PB) {
   264  		var buf bytes.Buffer
   265  		dec := NewDecoder(&buf)
   266  		var r codeResponse
   267  		for pb.Next() {
   268  			buf.Write(codeJSON)
   269  			// hide EOF
   270  			buf.WriteByte('\n')
   271  			buf.WriteByte('\n')
   272  			buf.WriteByte('\n')
   273  			if err := dec.Decode(&r); err != nil {
   274  				b.Fatalf("Decode error: %v", err)
   275  			}
   276  		}
   277  	})
   278  	b.SetBytes(int64(len(codeJSON)))
   279  }
   280  
   281  func BenchmarkUnicodeDecoder(b *testing.B) {
   282  	b.ReportAllocs()
   283  	j := []byte(`"\uD83D\uDE01"`)
   284  	b.SetBytes(int64(len(j)))
   285  	r := bytes.NewReader(j)
   286  	dec := NewDecoder(r)
   287  	var out string
   288  	b.ResetTimer()
   289  	for i := 0; i < b.N; i++ {
   290  		if err := dec.Decode(&out); err != nil {
   291  			b.Fatalf("Decode error: %v", err)
   292  		}
   293  		r.Seek(0, 0)
   294  	}
   295  }
   296  
   297  func BenchmarkDecoderStream(b *testing.B) {
   298  	b.ReportAllocs()
   299  	b.StopTimer()
   300  	var buf bytes.Buffer
   301  	dec := NewDecoder(&buf)
   302  	buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n")
   303  	var x any
   304  	if err := dec.Decode(&x); err != nil {
   305  		b.Fatalf("Decode error: %v", err)
   306  	}
   307  	ones := strings.Repeat(" 1\n", 300000) + "\n\n\n"
   308  	b.StartTimer()
   309  	for i := 0; i < b.N; i++ {
   310  		if i%300000 == 0 {
   311  			buf.WriteString(ones)
   312  		}
   313  		x = nil
   314  		switch err := dec.Decode(&x); {
   315  		case err != nil:
   316  			b.Fatalf("Decode error: %v", err)
   317  		case x != 1.0:
   318  			b.Fatalf("Decode: got %v want 1.0", i)
   319  		}
   320  	}
   321  }
   322  
   323  func BenchmarkCodeUnmarshal(b *testing.B) {
   324  	b.ReportAllocs()
   325  	if codeJSON == nil {
   326  		b.StopTimer()
   327  		codeInit()
   328  		b.StartTimer()
   329  	}
   330  	b.RunParallel(func(pb *testing.PB) {
   331  		for pb.Next() {
   332  			var r codeResponse
   333  			if err := Unmarshal(codeJSON, &r); err != nil {
   334  				b.Fatalf("Unmarshal error: %v", err)
   335  			}
   336  		}
   337  	})
   338  	b.SetBytes(int64(len(codeJSON)))
   339  }
   340  
   341  func BenchmarkCodeUnmarshalReuse(b *testing.B) {
   342  	b.ReportAllocs()
   343  	if codeJSON == nil {
   344  		b.StopTimer()
   345  		codeInit()
   346  		b.StartTimer()
   347  	}
   348  	b.RunParallel(func(pb *testing.PB) {
   349  		var r codeResponse
   350  		for pb.Next() {
   351  			if err := Unmarshal(codeJSON, &r); err != nil {
   352  				b.Fatalf("Unmarshal error: %v", err)
   353  			}
   354  		}
   355  	})
   356  	b.SetBytes(int64(len(codeJSON)))
   357  }
   358  
   359  func BenchmarkUnmarshalString(b *testing.B) {
   360  	b.ReportAllocs()
   361  	data := []byte(`"hello, world"`)
   362  	b.RunParallel(func(pb *testing.PB) {
   363  		var s string
   364  		for pb.Next() {
   365  			if err := Unmarshal(data, &s); err != nil {
   366  				b.Fatalf("Unmarshal error: %v", err)
   367  			}
   368  		}
   369  	})
   370  }
   371  
   372  func BenchmarkUnmarshalFloat64(b *testing.B) {
   373  	b.ReportAllocs()
   374  	data := []byte(`3.14`)
   375  	b.RunParallel(func(pb *testing.PB) {
   376  		var f float64
   377  		for pb.Next() {
   378  			if err := Unmarshal(data, &f); err != nil {
   379  				b.Fatalf("Unmarshal error: %v", err)
   380  			}
   381  		}
   382  	})
   383  }
   384  
   385  func BenchmarkUnmarshalInt64(b *testing.B) {
   386  	b.ReportAllocs()
   387  	data := []byte(`3`)
   388  	b.RunParallel(func(pb *testing.PB) {
   389  		var x int64
   390  		for pb.Next() {
   391  			if err := Unmarshal(data, &x); err != nil {
   392  				b.Fatalf("Unmarshal error: %v", err)
   393  			}
   394  		}
   395  	})
   396  }
   397  
   398  func BenchmarkUnmarshalMap(b *testing.B) {
   399  	b.ReportAllocs()
   400  	data := []byte(`{"key1":"value1","key2":"value2","key3":"value3"}`)
   401  	b.RunParallel(func(pb *testing.PB) {
   402  		x := make(map[string]string, 3)
   403  		for pb.Next() {
   404  			if err := Unmarshal(data, &x); err != nil {
   405  				b.Fatalf("Unmarshal error: %v", err)
   406  			}
   407  		}
   408  	})
   409  }
   410  
   411  func BenchmarkIssue10335(b *testing.B) {
   412  	b.ReportAllocs()
   413  	j := []byte(`{"a":{ }}`)
   414  	b.RunParallel(func(pb *testing.PB) {
   415  		var s struct{}
   416  		for pb.Next() {
   417  			if err := Unmarshal(j, &s); err != nil {
   418  				b.Fatalf("Unmarshal error: %v", err)
   419  			}
   420  		}
   421  	})
   422  }
   423  
   424  func BenchmarkIssue34127(b *testing.B) {
   425  	b.ReportAllocs()
   426  	j := struct {
   427  		Bar string `json:"bar,string"`
   428  	}{
   429  		Bar: `foobar`,
   430  	}
   431  	b.RunParallel(func(pb *testing.PB) {
   432  		for pb.Next() {
   433  			if _, err := Marshal(&j); err != nil {
   434  				b.Fatalf("Marshal error: %v", err)
   435  			}
   436  		}
   437  	})
   438  }
   439  
   440  func BenchmarkUnmapped(b *testing.B) {
   441  	b.ReportAllocs()
   442  	j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`)
   443  	b.RunParallel(func(pb *testing.PB) {
   444  		var s struct{}
   445  		for pb.Next() {
   446  			if err := Unmarshal(j, &s); err != nil {
   447  				b.Fatalf("Unmarshal error: %v", err)
   448  			}
   449  		}
   450  	})
   451  }
   452  
   453  func BenchmarkEncodeMarshaler(b *testing.B) {
   454  	b.ReportAllocs()
   455  
   456  	m := struct {
   457  		A int
   458  		B RawMessage
   459  	}{}
   460  
   461  	b.RunParallel(func(pb *testing.PB) {
   462  		enc := NewEncoder(io.Discard)
   463  
   464  		for pb.Next() {
   465  			if err := enc.Encode(&m); err != nil {
   466  				b.Fatalf("Encode error: %v", err)
   467  			}
   468  		}
   469  	})
   470  }
   471  
   472  func BenchmarkEncoderEncode(b *testing.B) {
   473  	b.ReportAllocs()
   474  	type T struct {
   475  		X, Y string
   476  	}
   477  	v := &T{"foo", "bar"}
   478  	b.RunParallel(func(pb *testing.PB) {
   479  		for pb.Next() {
   480  			if err := NewEncoder(io.Discard).Encode(v); err != nil {
   481  				b.Fatalf("Encode error: %v", err)
   482  			}
   483  		}
   484  	})
   485  }
   486  
   487  func BenchmarkNewEncoderEncode(b *testing.B) {
   488  	m := make(map[string]string)
   489  	for i := range 100_000 {
   490  		k := fmt.Sprintf("key%d", i)
   491  		v := fmt.Sprintf("%x", sha256.Sum256([]byte(k)))
   492  		m[k] = v
   493  	}
   494  	b.ResetTimer()
   495  	b.ReportAllocs()
   496  	for b.Loop() {
   497  		if err := NewEncoder(io.Discard).Encode(m); err != nil {
   498  			b.Fatalf("Encode error: %v", err)
   499  		}
   500  	}
   501  }
   502  

View as plain text