...

Source file src/go.mongodb.org/mongo-driver/bson/benchmark_test.go

Documentation: go.mongodb.org/mongo-driver/bson

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  package bson
     8  
     9  import (
    10  	"bytes"
    11  	"compress/gzip"
    12  	"encoding/json"
    13  	"fmt"
    14  	"io"
    15  	"io/ioutil"
    16  	"os"
    17  	"path"
    18  	"sync"
    19  	"testing"
    20  )
    21  
    22  type encodetest struct {
    23  	Field1String  string
    24  	Field1Int64   int64
    25  	Field1Float64 float64
    26  	Field2String  string
    27  	Field2Int64   int64
    28  	Field2Float64 float64
    29  	Field3String  string
    30  	Field3Int64   int64
    31  	Field3Float64 float64
    32  	Field4String  string
    33  	Field4Int64   int64
    34  	Field4Float64 float64
    35  }
    36  
    37  type nestedtest1 struct {
    38  	Nested nestedtest2
    39  }
    40  
    41  type nestedtest2 struct {
    42  	Nested nestedtest3
    43  }
    44  
    45  type nestedtest3 struct {
    46  	Nested nestedtest4
    47  }
    48  
    49  type nestedtest4 struct {
    50  	Nested nestedtest5
    51  }
    52  
    53  type nestedtest5 struct {
    54  	Nested nestedtest6
    55  }
    56  
    57  type nestedtest6 struct {
    58  	Nested nestedtest7
    59  }
    60  
    61  type nestedtest7 struct {
    62  	Nested nestedtest8
    63  }
    64  
    65  type nestedtest8 struct {
    66  	Nested nestedtest9
    67  }
    68  
    69  type nestedtest9 struct {
    70  	Nested nestedtest10
    71  }
    72  
    73  type nestedtest10 struct {
    74  	Nested nestedtest11
    75  }
    76  
    77  type nestedtest11 struct {
    78  	Nested encodetest
    79  }
    80  
    81  var encodetestInstance = encodetest{
    82  	Field1String:  "foo",
    83  	Field1Int64:   1,
    84  	Field1Float64: 3.0,
    85  	Field2String:  "bar",
    86  	Field2Int64:   2,
    87  	Field2Float64: 3.1,
    88  	Field3String:  "baz",
    89  	Field3Int64:   3,
    90  	Field3Float64: 3.14,
    91  	Field4String:  "qux",
    92  	Field4Int64:   4,
    93  	Field4Float64: 3.141,
    94  }
    95  
    96  var nestedInstance = nestedtest1{
    97  	nestedtest2{
    98  		nestedtest3{
    99  			nestedtest4{
   100  				nestedtest5{
   101  					nestedtest6{
   102  						nestedtest7{
   103  							nestedtest8{
   104  								nestedtest9{
   105  									nestedtest10{
   106  										nestedtest11{
   107  											encodetest{
   108  												Field1String:  "foo",
   109  												Field1Int64:   1,
   110  												Field1Float64: 3.0,
   111  												Field2String:  "bar",
   112  												Field2Int64:   2,
   113  												Field2Float64: 3.1,
   114  												Field3String:  "baz",
   115  												Field3Int64:   3,
   116  												Field3Float64: 3.14,
   117  												Field4String:  "qux",
   118  												Field4Int64:   4,
   119  												Field4Float64: 3.141,
   120  											},
   121  										},
   122  									},
   123  								},
   124  							},
   125  						},
   126  					},
   127  				},
   128  			},
   129  		},
   130  	},
   131  }
   132  
   133  const extendedBSONDir = "../testdata/extended_bson"
   134  
   135  var (
   136  	extJSONFiles   map[string]map[string]interface{}
   137  	extJSONFilesMu sync.Mutex
   138  )
   139  
   140  // readExtJSONFile reads the GZIP-compressed extended JSON document from the given filename in the
   141  // "extended BSON" test data directory (../testdata/extended_bson) and returns it as a
   142  // map[string]interface{}. It panics on any errors.
   143  func readExtJSONFile(filename string) map[string]interface{} {
   144  	extJSONFilesMu.Lock()
   145  	defer extJSONFilesMu.Unlock()
   146  	if v, ok := extJSONFiles[filename]; ok {
   147  		return v
   148  	}
   149  	filePath := path.Join(extendedBSONDir, filename)
   150  	file, err := os.Open(filePath)
   151  	if err != nil {
   152  		panic(fmt.Sprintf("error opening file %q: %s", filePath, err))
   153  	}
   154  	defer func() {
   155  		_ = file.Close()
   156  	}()
   157  
   158  	gz, err := gzip.NewReader(file)
   159  	if err != nil {
   160  		panic(fmt.Sprintf("error creating GZIP reader: %s", err))
   161  	}
   162  	defer func() {
   163  		_ = gz.Close()
   164  	}()
   165  
   166  	data, err := ioutil.ReadAll(gz)
   167  	if err != nil {
   168  		panic(fmt.Sprintf("error reading GZIP contents of file: %s", err))
   169  	}
   170  
   171  	var v map[string]interface{}
   172  	err = UnmarshalExtJSON(data, false, &v)
   173  	if err != nil {
   174  		panic(fmt.Sprintf("error unmarshalling extended JSON: %s", err))
   175  	}
   176  
   177  	if extJSONFiles == nil {
   178  		extJSONFiles = make(map[string]map[string]interface{})
   179  	}
   180  	extJSONFiles[filename] = v
   181  	return v
   182  }
   183  
   184  func BenchmarkMarshal(b *testing.B) {
   185  	cases := []struct {
   186  		desc  string
   187  		value interface{}
   188  	}{
   189  		{
   190  			desc:  "simple struct",
   191  			value: encodetestInstance,
   192  		},
   193  		{
   194  			desc:  "nested struct",
   195  			value: nestedInstance,
   196  		},
   197  		{
   198  			desc:  "deep_bson.json.gz",
   199  			value: readExtJSONFile("deep_bson.json.gz"),
   200  		},
   201  		{
   202  			desc:  "flat_bson.json.gz",
   203  			value: readExtJSONFile("flat_bson.json.gz"),
   204  		},
   205  		{
   206  			desc:  "full_bson.json.gz",
   207  			value: readExtJSONFile("full_bson.json.gz"),
   208  		},
   209  	}
   210  
   211  	for _, tc := range cases {
   212  		b.Run(tc.desc, func(b *testing.B) {
   213  			b.Run("BSON", func(b *testing.B) {
   214  				for i := 0; i < b.N; i++ {
   215  					_, err := Marshal(tc.value)
   216  					if err != nil {
   217  						b.Errorf("error marshalling BSON: %s", err)
   218  					}
   219  				}
   220  			})
   221  
   222  			b.Run("extJSON", func(b *testing.B) {
   223  				for i := 0; i < b.N; i++ {
   224  					_, err := MarshalExtJSON(tc.value, true, false)
   225  					if err != nil {
   226  						b.Errorf("error marshalling extended JSON: %s", err)
   227  					}
   228  				}
   229  			})
   230  
   231  			b.Run("JSON", func(b *testing.B) {
   232  				for i := 0; i < b.N; i++ {
   233  					_, err := json.Marshal(tc.value)
   234  					if err != nil {
   235  						b.Errorf("error marshalling JSON: %s", err)
   236  					}
   237  				}
   238  			})
   239  		})
   240  	}
   241  }
   242  
   243  func BenchmarkUnmarshal(b *testing.B) {
   244  	cases := []struct {
   245  		desc  string
   246  		value interface{}
   247  	}{
   248  		{
   249  			desc:  "simple struct",
   250  			value: encodetestInstance,
   251  		},
   252  		{
   253  			desc:  "nested struct",
   254  			value: nestedInstance,
   255  		},
   256  		{
   257  			desc:  "deep_bson.json.gz",
   258  			value: readExtJSONFile("deep_bson.json.gz"),
   259  		},
   260  		{
   261  			desc:  "flat_bson.json.gz",
   262  			value: readExtJSONFile("flat_bson.json.gz"),
   263  		},
   264  		{
   265  			desc:  "full_bson.json.gz",
   266  			value: readExtJSONFile("full_bson.json.gz"),
   267  		},
   268  	}
   269  
   270  	for _, tc := range cases {
   271  		b.Run(tc.desc, func(b *testing.B) {
   272  			b.Run("BSON", func(b *testing.B) {
   273  				data, err := Marshal(tc.value)
   274  				if err != nil {
   275  					b.Errorf("error marshalling BSON: %s", err)
   276  					return
   277  				}
   278  
   279  				b.ResetTimer()
   280  				var v2 map[string]interface{}
   281  				for i := 0; i < b.N; i++ {
   282  					err := Unmarshal(data, &v2)
   283  					if err != nil {
   284  						b.Errorf("error unmarshalling BSON: %s", err)
   285  					}
   286  				}
   287  			})
   288  
   289  			b.Run("extJSON", func(b *testing.B) {
   290  				data, err := MarshalExtJSON(tc.value, true, false)
   291  				if err != nil {
   292  					b.Errorf("error marshalling extended JSON: %s", err)
   293  					return
   294  				}
   295  
   296  				b.ResetTimer()
   297  				var v2 map[string]interface{}
   298  				for i := 0; i < b.N; i++ {
   299  					err := UnmarshalExtJSON(data, true, &v2)
   300  					if err != nil {
   301  						b.Errorf("error unmarshalling extended JSON: %s", err)
   302  					}
   303  				}
   304  			})
   305  
   306  			b.Run("JSON", func(b *testing.B) {
   307  				data, err := json.Marshal(tc.value)
   308  				if err != nil {
   309  					b.Errorf("error marshalling JSON: %s", err)
   310  					return
   311  				}
   312  
   313  				b.ResetTimer()
   314  				var v2 map[string]interface{}
   315  				for i := 0; i < b.N; i++ {
   316  					err := json.Unmarshal(data, &v2)
   317  					if err != nil {
   318  						b.Errorf("error unmarshalling JSON: %s", err)
   319  					}
   320  				}
   321  			})
   322  		})
   323  	}
   324  }
   325  
   326  // The following benchmarks are copied from the Go standard library's
   327  // encoding/json package.
   328  
   329  type codeResponse struct {
   330  	Tree     *codeNode `json:"tree"`
   331  	Username string    `json:"username"`
   332  }
   333  
   334  type codeNode struct {
   335  	Name     string      `json:"name"`
   336  	Kids     []*codeNode `json:"kids"`
   337  	CLWeight float64     `json:"cl_weight"`
   338  	Touches  int         `json:"touches"`
   339  	MinT     int64       `json:"min_t"`
   340  	MaxT     int64       `json:"max_t"`
   341  	MeanT    int64       `json:"mean_t"`
   342  }
   343  
   344  var codeJSON []byte
   345  var codeBSON []byte
   346  var codeStruct codeResponse
   347  
   348  func codeInit() {
   349  	f, err := os.Open("testdata/code.json.gz")
   350  	if err != nil {
   351  		panic(err)
   352  	}
   353  	defer f.Close()
   354  	gz, err := gzip.NewReader(f)
   355  	if err != nil {
   356  		panic(err)
   357  	}
   358  	data, err := io.ReadAll(gz)
   359  	if err != nil {
   360  		panic(err)
   361  	}
   362  
   363  	codeJSON = data
   364  
   365  	if err := json.Unmarshal(codeJSON, &codeStruct); err != nil {
   366  		panic("json.Unmarshal code.json: " + err.Error())
   367  	}
   368  
   369  	if data, err = json.Marshal(&codeStruct); err != nil {
   370  		panic("json.Marshal code.json: " + err.Error())
   371  	}
   372  
   373  	if codeBSON, err = Marshal(&codeStruct); err != nil {
   374  		panic("Marshal code.json: " + err.Error())
   375  	}
   376  
   377  	if !bytes.Equal(data, codeJSON) {
   378  		println("different lengths", len(data), len(codeJSON))
   379  		for i := 0; i < len(data) && i < len(codeJSON); i++ {
   380  			if data[i] != codeJSON[i] {
   381  				println("re-marshal: changed at byte", i)
   382  				println("orig: ", string(codeJSON[i-10:i+10]))
   383  				println("new: ", string(data[i-10:i+10]))
   384  				break
   385  			}
   386  		}
   387  		panic("re-marshal code.json: different result")
   388  	}
   389  }
   390  
   391  func BenchmarkCodeUnmarshal(b *testing.B) {
   392  	b.ReportAllocs()
   393  	if codeJSON == nil {
   394  		b.StopTimer()
   395  		codeInit()
   396  		b.StartTimer()
   397  	}
   398  	b.Run("BSON", func(b *testing.B) {
   399  		b.RunParallel(func(pb *testing.PB) {
   400  			for pb.Next() {
   401  				var r codeResponse
   402  				if err := Unmarshal(codeBSON, &r); err != nil {
   403  					b.Fatal("Unmarshal:", err)
   404  				}
   405  			}
   406  		})
   407  		b.SetBytes(int64(len(codeBSON)))
   408  	})
   409  	b.Run("JSON", func(b *testing.B) {
   410  		b.RunParallel(func(pb *testing.PB) {
   411  			for pb.Next() {
   412  				var r codeResponse
   413  				if err := json.Unmarshal(codeJSON, &r); err != nil {
   414  					b.Fatal("json.Unmarshal:", err)
   415  				}
   416  			}
   417  		})
   418  		b.SetBytes(int64(len(codeJSON)))
   419  	})
   420  }
   421  
   422  func BenchmarkCodeMarshal(b *testing.B) {
   423  	b.ReportAllocs()
   424  	if codeJSON == nil {
   425  		b.StopTimer()
   426  		codeInit()
   427  		b.StartTimer()
   428  	}
   429  	b.Run("BSON", func(b *testing.B) {
   430  		b.RunParallel(func(pb *testing.PB) {
   431  			for pb.Next() {
   432  				if _, err := Marshal(&codeStruct); err != nil {
   433  					b.Fatal("Marshal:", err)
   434  				}
   435  			}
   436  		})
   437  		b.SetBytes(int64(len(codeBSON)))
   438  	})
   439  	b.Run("JSON", func(b *testing.B) {
   440  		b.RunParallel(func(pb *testing.PB) {
   441  			for pb.Next() {
   442  				if _, err := json.Marshal(&codeStruct); err != nil {
   443  					b.Fatal("json.Marshal:", err)
   444  				}
   445  			}
   446  		})
   447  		b.SetBytes(int64(len(codeJSON)))
   448  	})
   449  }
   450  

View as plain text