github.com/wI2L/jettison@v0.7.5-0.20230106001914-c70014c6417a/bench_test.go (about)

     1  package jettison
     2  
     3  import (
     4  	"bytes"
     5  	"compress/gzip"
     6  	"context"
     7  	"encoding/json"
     8  	"io/ioutil"
     9  	"os"
    10  	"strconv"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	jsoniter "github.com/json-iterator/go"
    16  	segmentj "github.com/segmentio/encoding/json"
    17  )
    18  
    19  var jsoniterCfg = jsoniter.ConfigCompatibleWithStandardLibrary
    20  
    21  type marshalFunc func(interface{}) ([]byte, error)
    22  
    23  type codeResponse struct {
    24  	Tree     *codeNode `json:"tree"`
    25  	Username string    `json:"username"`
    26  }
    27  
    28  type codeNode struct {
    29  	Name     string      `json:"name"`
    30  	Kids     []*codeNode `json:"kids"`
    31  	CLWeight float64     `json:"cl_weight"`
    32  	Touches  int         `json:"touches"`
    33  	MinT     int64       `json:"min_t"`
    34  	MaxT     int64       `json:"max_t"`
    35  	MeanT    int64       `json:"mean_t"`
    36  }
    37  
    38  type simplePayload struct {
    39  	St   int    `json:"st"`
    40  	Sid  int    `json:"sid"`
    41  	Tt   string `json:"tt"`
    42  	Gr   int    `json:"gr"`
    43  	UUID string `json:"uuid"`
    44  	IP   string `json:"ip"`
    45  	Ua   string `json:"ua"`
    46  	Tz   int    `json:"tz"`
    47  	V    bool   `json:"v"`
    48  }
    49  
    50  func BenchmarkSimple(b *testing.B) {
    51  	sp := &simplePayload{
    52  		St:   1,
    53  		Sid:  2,
    54  		Tt:   "TestString",
    55  		Gr:   4,
    56  		UUID: "8f9a65eb-4807-4d57-b6e0-bda5d62f1429",
    57  		IP:   "127.0.0.1",
    58  		Ua:   "Mozilla",
    59  		Tz:   8,
    60  		V:    true,
    61  	}
    62  	benchMarshal(b, sp)
    63  }
    64  
    65  func BenchmarkComplex(b *testing.B) {
    66  	benchMarshal(b, xx)
    67  }
    68  
    69  func BenchmarkCodeMarshal(b *testing.B) {
    70  	// Taken from the encoding/json package.
    71  	x := codeInit(b)
    72  	benchMarshal(b, x)
    73  }
    74  
    75  func BenchmarkMap(b *testing.B) {
    76  	m := map[string]int{
    77  		"Cassianus": 1,
    78  		"Ludovicus": 42,
    79  		"Flavius":   8990,
    80  		"Baldwin":   345,
    81  		"Agapios":   -43,
    82  		"Liberia":   0,
    83  	}
    84  	benchMarshal(b, m)
    85  	benchMarshalOpts(b, "jettison-nosort", m, UnsortedMap())
    86  }
    87  
    88  func BenchmarkSyncMap(b *testing.B) {
    89  	if testing.Short() {
    90  		b.SkipNow()
    91  	}
    92  	var sm sync.Map
    93  
    94  	sm.Store("a", "foobar")
    95  	sm.Store("b", 42)
    96  	sm.Store("c", false)
    97  	sm.Store("d", float64(3.14159))
    98  
    99  	benchMarshalOpts(b, "sorted", m)
   100  	benchMarshalOpts(b, "unsorted", m, UnsortedMap())
   101  }
   102  
   103  func BenchmarkDuration(b *testing.B) {
   104  	if testing.Short() {
   105  		b.SkipNow()
   106  	}
   107  	d := 1337 * time.Second
   108  	benchMarshal(b, d)
   109  }
   110  
   111  func BenchmarkDurationFormat(b *testing.B) {
   112  	if testing.Short() {
   113  		b.SkipNow()
   114  	}
   115  	d := 32*time.Hour + 56*time.Minute + 25*time.Second
   116  	for _, f := range []DurationFmt{
   117  		DurationString,
   118  		DurationMinutes,
   119  		DurationSeconds,
   120  		DurationMicroseconds,
   121  		DurationMilliseconds,
   122  		DurationNanoseconds,
   123  	} {
   124  		benchMarshalOpts(b, f.String(), d, DurationFormat(f))
   125  	}
   126  }
   127  
   128  func BenchmarkTime(b *testing.B) {
   129  	if testing.Short() {
   130  		b.SkipNow()
   131  	}
   132  	t := time.Now()
   133  	benchMarshal(b, t)
   134  }
   135  
   136  func BenchmarkStringEscaping(b *testing.B) {
   137  	if testing.Short() {
   138  		b.SkipNow()
   139  	}
   140  	s := "<ŁØŘ€M ƗƤŞỮM ĐØŁØŘ ŞƗŦ ΔM€Ŧ>"
   141  
   142  	benchMarshalOpts(b, "Full", s)
   143  	benchMarshalOpts(b, "NoUTF8Coercion", s, NoUTF8Coercion())
   144  	benchMarshalOpts(b, "NoHTMLEscaping", s, NoHTMLEscaping())
   145  	benchMarshalOpts(b, "NoUTF8Coercion/NoHTMLEscaping", s, NoUTF8Coercion(), NoHTMLEscaping())
   146  	benchMarshalOpts(b, "NoStringEscaping", s, NoStringEscaping())
   147  }
   148  
   149  type (
   150  	jsonbm    struct{}
   151  	textbm    struct{}
   152  	jetibm    struct{}
   153  	jetictxbm struct{}
   154  )
   155  
   156  var (
   157  	loreumipsum  = "Lorem ipsum dolor sit amet"
   158  	loreumipsumQ = strconv.Quote(loreumipsum)
   159  )
   160  
   161  func (jsonbm) MarshalJSON() ([]byte, error)          { return []byte(loreumipsumQ), nil }
   162  func (textbm) MarshalText() ([]byte, error)          { return []byte(loreumipsum), nil }
   163  func (jetibm) AppendJSON(dst []byte) ([]byte, error) { return append(dst, loreumipsum...), nil }
   164  
   165  func (jetictxbm) AppendJSONContext(_ context.Context, dst []byte) ([]byte, error) {
   166  	return append(dst, loreumipsum...), nil
   167  }
   168  
   169  func BenchmarkMarshaler(b *testing.B) {
   170  	if testing.Short() {
   171  		b.SkipNow()
   172  	}
   173  	for _, bb := range []struct {
   174  		name string
   175  		impl interface{}
   176  		opts []Option
   177  	}{
   178  		{"json", jsonbm{}, nil},
   179  		{"text", textbm{}, nil},
   180  		{"append", jetibm{}, nil},
   181  		{"appendctx", jetictxbm{}, []Option{WithContext(context.Background())}},
   182  	} {
   183  		benchMarshalOpts(b, bb.name, bb.impl, bb.opts...)
   184  	}
   185  }
   186  
   187  func codeInit(b *testing.B) *codeResponse {
   188  	f, err := os.Open("testdata/code.json.gz")
   189  	if err != nil {
   190  		b.Fatal(err)
   191  	}
   192  	defer f.Close()
   193  	gz, err := gzip.NewReader(f)
   194  	if err != nil {
   195  		b.Fatal(err)
   196  	}
   197  	data, err := ioutil.ReadAll(gz)
   198  	if err != nil {
   199  		b.Fatal(err)
   200  	}
   201  	codeJSON := data
   202  
   203  	var resp codeResponse
   204  	if err := json.Unmarshal(codeJSON, &resp); err != nil {
   205  		b.Fatalf("unmarshal code.json: %s", err)
   206  	}
   207  	if data, err = Marshal(&resp); err != nil {
   208  		b.Fatalf("marshal code.json: %s", err)
   209  	}
   210  	if !bytes.Equal(data, codeJSON) {
   211  		b.Logf("different lengths: %d - %d", len(data), len(codeJSON))
   212  
   213  		for i := 0; i < len(data) && i < len(codeJSON); i++ {
   214  			if data[i] != codeJSON[i] {
   215  				b.Logf("re-marshal: changed at byte %d", i)
   216  				b.Logf("old: %s", string(codeJSON[i-10:i+10]))
   217  				b.Logf("new: %s", string(data[i-10:i+10]))
   218  				break
   219  			}
   220  		}
   221  		b.Fatal("re-marshal code.json: different result")
   222  	}
   223  	return &resp
   224  }
   225  
   226  func benchMarshalOpts(b *testing.B, name string, x interface{}, opts ...Option) {
   227  	b.Run(name, func(b *testing.B) {
   228  		b.ReportAllocs()
   229  		for i := 0; i < b.N; i++ {
   230  			bts, err := MarshalOpts(x, opts...)
   231  			if err != nil {
   232  				b.Fatal(err)
   233  			}
   234  			b.SetBytes(int64(len(bts)))
   235  		}
   236  	})
   237  }
   238  
   239  func benchMarshal(b *testing.B, x interface{}) {
   240  	for _, bb := range []struct {
   241  		name string
   242  		fn   marshalFunc
   243  	}{
   244  		{"standard", json.Marshal},
   245  		{"jsoniter", jsoniterCfg.Marshal},
   246  		{"segmentj", segmentj.Marshal},
   247  		{"jettison", Marshal},
   248  	} {
   249  		bb := bb
   250  		b.Run(bb.name, func(b *testing.B) {
   251  			b.ReportAllocs()
   252  			for i := 0; i < b.N; i++ {
   253  				bts, err := bb.fn(x)
   254  				if err != nil {
   255  					b.Error(err)
   256  				}
   257  				b.SetBytes(int64(len(bts)))
   258  			}
   259  		})
   260  	}
   261  }