bench_test.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. // Copyright (c) 2012, 2013 Ugorji Nwoke. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license found in the LICENSE file.
  3. package codec
  4. import (
  5. "bytes"
  6. "encoding/gob"
  7. "encoding/json"
  8. "flag"
  9. "fmt"
  10. "reflect"
  11. "runtime"
  12. "testing"
  13. "time"
  14. )
  15. // Sample way to run:
  16. // go test -bi -bv -bd=1 -benchmem -bench=.
  17. var (
  18. _ = fmt.Printf
  19. benchTs *TestStruc
  20. approxSize int
  21. benchDoInitBench bool
  22. benchVerify bool
  23. benchUnscientificRes bool = false
  24. //depth of 0 maps to ~400bytes json-encoded string, 1 maps to ~1400 bytes, etc
  25. //For depth>1, we likely trigger stack growth for encoders, making benchmarking unreliable.
  26. benchDepth int
  27. benchInitDebug bool
  28. benchCheckers []benchChecker
  29. )
  30. type benchEncFn func(interface{}) ([]byte, error)
  31. type benchDecFn func([]byte, interface{}) error
  32. type benchIntfFn func() interface{}
  33. type benchChecker struct {
  34. name string
  35. encodefn benchEncFn
  36. decodefn benchDecFn
  37. }
  38. func benchInitFlags() {
  39. flag.BoolVar(&benchInitDebug, "bg", false, "Bench Debug")
  40. flag.IntVar(&benchDepth, "bd", 1, "Bench Depth: If >1, potential unreliable results due to stack growth")
  41. flag.BoolVar(&benchDoInitBench, "bi", false, "Run Bench Init")
  42. flag.BoolVar(&benchVerify, "bv", false, "Verify Decoded Value during Benchmark")
  43. flag.BoolVar(&benchUnscientificRes, "bu", false, "Show Unscientific Results during Benchmark")
  44. }
  45. func benchInit() {
  46. benchTs = newTestStruc(benchDepth, true)
  47. approxSize = approxDataSize(reflect.ValueOf(benchTs))
  48. bytesLen := 1024 * 4 * (benchDepth + 1) * (benchDepth + 1)
  49. if bytesLen < approxSize {
  50. bytesLen = approxSize
  51. }
  52. benchCheckers = append(benchCheckers,
  53. benchChecker{"msgpack", fnMsgpackEncodeFn, fnMsgpackDecodeFn},
  54. benchChecker{"binc", fnBincEncodeFn, fnBincDecodeFn},
  55. benchChecker{"gob", fnGobEncodeFn, fnGobDecodeFn},
  56. benchChecker{"json", fnJsonEncodeFn, fnJsonDecodeFn},
  57. )
  58. if benchDoInitBench {
  59. runBenchInit()
  60. }
  61. }
  62. func runBenchInit() {
  63. logT(nil, "..............................................")
  64. logT(nil, "BENCHMARK INIT: %v", time.Now())
  65. logT(nil, "To run full benchmark comparing encodings (MsgPack, Binc, JSON, GOB, etc), "+
  66. "use: \"go test -bench=.\"")
  67. logT(nil, "Benchmark: ")
  68. logT(nil, "\tStruct recursive Depth: %d", benchDepth)
  69. if approxSize > 0 {
  70. logT(nil, "\tApproxDeepSize Of benchmark Struct: %d bytes", approxSize)
  71. }
  72. if benchUnscientificRes {
  73. logT(nil, "Benchmark One-Pass Run (with Unscientific Encode/Decode times): ")
  74. } else {
  75. logT(nil, "Benchmark One-Pass Run:")
  76. }
  77. for _, bc := range benchCheckers {
  78. doBenchCheck(bc.name, bc.encodefn, bc.decodefn)
  79. }
  80. logT(nil, "..............................................")
  81. if benchInitDebug {
  82. logT(nil, "<<<<====>>>> depth: %v, ts: %#v\n", benchDepth, benchTs)
  83. }
  84. }
  85. func fnBenchNewTs() interface{} {
  86. return new(TestStruc)
  87. }
  88. func doBenchCheck(name string, encfn benchEncFn, decfn benchDecFn) {
  89. runtime.GC()
  90. tnow := time.Now()
  91. buf, err := encfn(benchTs)
  92. if err != nil {
  93. logT(nil, "\t%10s: **** Error encoding benchTs: %v", name, err)
  94. }
  95. encDur := time.Now().Sub(tnow)
  96. encLen := len(buf)
  97. runtime.GC()
  98. if !benchUnscientificRes {
  99. logT(nil, "\t%10s: len: %d bytes\n", name, encLen)
  100. return
  101. }
  102. tnow = time.Now()
  103. if err = decfn(buf, new(TestStruc)); err != nil {
  104. logT(nil, "\t%10s: **** Error decoding into new TestStruc: %v", name, err)
  105. }
  106. decDur := time.Now().Sub(tnow)
  107. logT(nil, "\t%10s: len: %d bytes, encode: %v, decode: %v\n", name, encLen, encDur, decDur)
  108. }
  109. func fnBenchmarkEncode(b *testing.B, encName string, ts interface{}, encfn benchEncFn) {
  110. runtime.GC()
  111. b.ResetTimer()
  112. for i := 0; i < b.N; i++ {
  113. _, err := encfn(ts)
  114. if err != nil {
  115. logT(b, "Error encoding benchTs: %s: %v", encName, err)
  116. b.FailNow()
  117. }
  118. }
  119. }
  120. func fnBenchmarkDecode(b *testing.B, encName string, ts interface{},
  121. encfn benchEncFn, decfn benchDecFn, newfn benchIntfFn,
  122. ) {
  123. buf, err := encfn(ts)
  124. if err != nil {
  125. logT(b, "Error encoding benchTs: %s: %v", encName, err)
  126. b.FailNow()
  127. }
  128. runtime.GC()
  129. b.ResetTimer()
  130. for i := 0; i < b.N; i++ {
  131. ts = newfn()
  132. if err = decfn(buf, ts); err != nil {
  133. logT(b, "Error decoding into new TestStruc: %s: %v", encName, err)
  134. b.FailNow()
  135. }
  136. if benchVerify {
  137. if vts, vok := ts.(*TestStruc); vok {
  138. verifyTsTree(b, vts)
  139. }
  140. }
  141. }
  142. }
  143. func verifyTsTree(b *testing.B, ts *TestStruc) {
  144. var ts0, ts1m, ts2m, ts1s, ts2s *TestStruc
  145. ts0 = ts
  146. if benchDepth > 0 {
  147. ts1m, ts1s = verifyCheckAndGet(b, ts0)
  148. }
  149. if benchDepth > 1 {
  150. ts2m, ts2s = verifyCheckAndGet(b, ts1m)
  151. }
  152. for _, tsx := range []*TestStruc{ts0, ts1m, ts2m, ts1s, ts2s} {
  153. if tsx != nil {
  154. verifyOneOne(b, tsx)
  155. }
  156. }
  157. }
  158. func verifyCheckAndGet(b *testing.B, ts0 *TestStruc) (ts1m *TestStruc, ts1s *TestStruc) {
  159. // if len(ts1m.Ms) <= 2 {
  160. // logT(b, "Error: ts1m.Ms len should be > 2. Got: %v", len(ts1m.Ms))
  161. // b.FailNow()
  162. // }
  163. if len(ts0.Its) == 0 {
  164. logT(b, "Error: ts0.Islice len should be > 0. Got: %v", len(ts0.Its))
  165. b.FailNow()
  166. }
  167. ts1m = ts0.Mtsptr["0"]
  168. ts1s = ts0.Its[0]
  169. if ts1m == nil || ts1s == nil {
  170. logT(b, "Error: At benchDepth 1, No *TestStruc found")
  171. b.FailNow()
  172. }
  173. return
  174. }
  175. func verifyOneOne(b *testing.B, ts *TestStruc) {
  176. if ts.I64slice[2] != int64(3) {
  177. logT(b, "Error: Decode failed by checking values")
  178. b.FailNow()
  179. }
  180. }
  181. func fnMsgpackEncodeFn(ts interface{}) (bs []byte, err error) {
  182. err = NewEncoderBytes(&bs, testMsgpackH).Encode(ts)
  183. return
  184. }
  185. func fnMsgpackDecodeFn(buf []byte, ts interface{}) error {
  186. return NewDecoderBytes(buf, testMsgpackH).Decode(ts)
  187. }
  188. func fnBincEncodeFn(ts interface{}) (bs []byte, err error) {
  189. err = NewEncoderBytes(&bs, testBincH).Encode(ts)
  190. return
  191. }
  192. func fnBincDecodeFn(buf []byte, ts interface{}) error {
  193. return NewDecoderBytes(buf, testBincH).Decode(ts)
  194. }
  195. func fnGobEncodeFn(ts interface{}) ([]byte, error) {
  196. bbuf := new(bytes.Buffer)
  197. err := gob.NewEncoder(bbuf).Encode(ts)
  198. return bbuf.Bytes(), err
  199. }
  200. func fnGobDecodeFn(buf []byte, ts interface{}) error {
  201. return gob.NewDecoder(bytes.NewBuffer(buf)).Decode(ts)
  202. }
  203. func fnJsonEncodeFn(ts interface{}) ([]byte, error) {
  204. return json.Marshal(ts)
  205. }
  206. func fnJsonDecodeFn(buf []byte, ts interface{}) error {
  207. return json.Unmarshal(buf, ts)
  208. }
  209. func Benchmark__Msgpack____Encode(b *testing.B) {
  210. fnBenchmarkEncode(b, "msgpack", benchTs, fnMsgpackEncodeFn)
  211. }
  212. func Benchmark__Msgpack____Decode(b *testing.B) {
  213. fnBenchmarkDecode(b, "msgpack", benchTs, fnMsgpackEncodeFn, fnMsgpackDecodeFn, fnBenchNewTs)
  214. }
  215. func Benchmark__Binc_NoSym_Encode(b *testing.B) {
  216. tSym := testBincH.AsSymbols
  217. testBincH.AsSymbols = AsSymbolNone
  218. fnBenchmarkEncode(b, "binc", benchTs, fnBincEncodeFn)
  219. testBincH.AsSymbols = tSym
  220. }
  221. func Benchmark__Binc_NoSym_Decode(b *testing.B) {
  222. tSym := testBincH.AsSymbols
  223. testBincH.AsSymbols = AsSymbolNone
  224. fnBenchmarkDecode(b, "binc", benchTs, fnBincEncodeFn, fnBincDecodeFn, fnBenchNewTs)
  225. testBincH.AsSymbols = tSym
  226. }
  227. func Benchmark__Binc_Sym___Encode(b *testing.B) {
  228. tSym := testBincH.AsSymbols
  229. testBincH.AsSymbols = AsSymbolAll
  230. fnBenchmarkEncode(b, "binc", benchTs, fnBincEncodeFn)
  231. testBincH.AsSymbols = tSym
  232. }
  233. func Benchmark__Binc_Sym___Decode(b *testing.B) {
  234. tSym := testBincH.AsSymbols
  235. testBincH.AsSymbols = AsSymbolAll
  236. fnBenchmarkDecode(b, "binc", benchTs, fnBincEncodeFn, fnBincDecodeFn, fnBenchNewTs)
  237. testBincH.AsSymbols = tSym
  238. }
  239. func Benchmark__Gob________Encode(b *testing.B) {
  240. fnBenchmarkEncode(b, "gob", benchTs, fnGobEncodeFn)
  241. }
  242. func Benchmark__Gob________Decode(b *testing.B) {
  243. fnBenchmarkDecode(b, "gob", benchTs, fnGobEncodeFn, fnGobDecodeFn, fnBenchNewTs)
  244. }
  245. func Benchmark__Json_______Encode(b *testing.B) {
  246. fnBenchmarkEncode(b, "json", benchTs, fnJsonEncodeFn)
  247. }
  248. func Benchmark__Json_______Decode(b *testing.B) {
  249. fnBenchmarkDecode(b, "json", benchTs, fnJsonEncodeFn, fnJsonDecodeFn, fnBenchNewTs)
  250. }