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 }