bench_test.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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-nosym", fnBincNoSymEncodeFn, fnBincNoSymDecodeFn},
  55. benchChecker{"binc-sym", fnBincSymEncodeFn, fnBincSymDecodeFn},
  56. benchChecker{"simple", fnSimpleEncodeFn, fnSimpleDecodeFn},
  57. benchChecker{"gob", fnGobEncodeFn, fnGobDecodeFn},
  58. benchChecker{"json", fnJsonEncodeFn, fnJsonDecodeFn},
  59. )
  60. if benchDoInitBench {
  61. runBenchInit()
  62. }
  63. }
  64. func runBenchInit() {
  65. logT(nil, "..............................................")
  66. logT(nil, "BENCHMARK INIT: %v", time.Now())
  67. logT(nil, "To run full benchmark comparing encodings (MsgPack, Binc, Simple, JSON, GOB, etc), "+
  68. "use: \"go test -bench=.\"")
  69. logT(nil, "Benchmark: ")
  70. logT(nil, "\tStruct recursive Depth: %d", benchDepth)
  71. if approxSize > 0 {
  72. logT(nil, "\tApproxDeepSize Of benchmark Struct: %d bytes", approxSize)
  73. }
  74. if benchUnscientificRes {
  75. logT(nil, "Benchmark One-Pass Run (with Unscientific Encode/Decode times): ")
  76. } else {
  77. logT(nil, "Benchmark One-Pass Run:")
  78. }
  79. for _, bc := range benchCheckers {
  80. doBenchCheck(bc.name, bc.encodefn, bc.decodefn)
  81. }
  82. logT(nil, "..............................................")
  83. if benchInitDebug {
  84. logT(nil, "<<<<====>>>> depth: %v, ts: %#v\n", benchDepth, benchTs)
  85. }
  86. }
  87. func fnBenchNewTs() interface{} {
  88. return new(TestStruc)
  89. }
  90. func doBenchCheck(name string, encfn benchEncFn, decfn benchDecFn) {
  91. runtime.GC()
  92. tnow := time.Now()
  93. buf, err := encfn(benchTs)
  94. if err != nil {
  95. logT(nil, "\t%10s: **** Error encoding benchTs: %v", name, err)
  96. }
  97. encDur := time.Now().Sub(tnow)
  98. encLen := len(buf)
  99. runtime.GC()
  100. if !benchUnscientificRes {
  101. logT(nil, "\t%10s: len: %d bytes\n", name, encLen)
  102. return
  103. }
  104. tnow = time.Now()
  105. if err = decfn(buf, new(TestStruc)); err != nil {
  106. logT(nil, "\t%10s: **** Error decoding into new TestStruc: %v", name, err)
  107. }
  108. decDur := time.Now().Sub(tnow)
  109. logT(nil, "\t%10s: len: %d bytes, encode: %v, decode: %v\n", name, encLen, encDur, decDur)
  110. }
  111. func fnBenchmarkEncode(b *testing.B, encName string, ts interface{}, encfn benchEncFn) {
  112. runtime.GC()
  113. b.ResetTimer()
  114. for i := 0; i < b.N; i++ {
  115. _, err := encfn(ts)
  116. if err != nil {
  117. logT(b, "Error encoding benchTs: %s: %v", encName, err)
  118. b.FailNow()
  119. }
  120. }
  121. }
  122. func fnBenchmarkDecode(b *testing.B, encName string, ts interface{},
  123. encfn benchEncFn, decfn benchDecFn, newfn benchIntfFn,
  124. ) {
  125. buf, err := encfn(ts)
  126. if err != nil {
  127. logT(b, "Error encoding benchTs: %s: %v", encName, err)
  128. b.FailNow()
  129. }
  130. runtime.GC()
  131. b.ResetTimer()
  132. for i := 0; i < b.N; i++ {
  133. ts = newfn()
  134. if err = decfn(buf, ts); err != nil {
  135. logT(b, "Error decoding into new TestStruc: %s: %v", encName, err)
  136. b.FailNow()
  137. }
  138. if benchVerify {
  139. if vts, vok := ts.(*TestStruc); vok {
  140. verifyTsTree(b, vts)
  141. }
  142. }
  143. }
  144. }
  145. func verifyTsTree(b *testing.B, ts *TestStruc) {
  146. var ts0, ts1m, ts2m, ts1s, ts2s *TestStruc
  147. ts0 = ts
  148. if benchDepth > 0 {
  149. ts1m, ts1s = verifyCheckAndGet(b, ts0)
  150. }
  151. if benchDepth > 1 {
  152. ts2m, ts2s = verifyCheckAndGet(b, ts1m)
  153. }
  154. for _, tsx := range []*TestStruc{ts0, ts1m, ts2m, ts1s, ts2s} {
  155. if tsx != nil {
  156. verifyOneOne(b, tsx)
  157. }
  158. }
  159. }
  160. func verifyCheckAndGet(b *testing.B, ts0 *TestStruc) (ts1m *TestStruc, ts1s *TestStruc) {
  161. // if len(ts1m.Ms) <= 2 {
  162. // logT(b, "Error: ts1m.Ms len should be > 2. Got: %v", len(ts1m.Ms))
  163. // b.FailNow()
  164. // }
  165. if len(ts0.Its) == 0 {
  166. logT(b, "Error: ts0.Islice len should be > 0. Got: %v", len(ts0.Its))
  167. b.FailNow()
  168. }
  169. ts1m = ts0.Mtsptr["0"]
  170. ts1s = ts0.Its[0]
  171. if ts1m == nil || ts1s == nil {
  172. logT(b, "Error: At benchDepth 1, No *TestStruc found")
  173. b.FailNow()
  174. }
  175. return
  176. }
  177. func verifyOneOne(b *testing.B, ts *TestStruc) {
  178. if ts.I64slice[2] != int64(3) {
  179. logT(b, "Error: Decode failed by checking values")
  180. b.FailNow()
  181. }
  182. }
  183. func fnMsgpackEncodeFn(ts interface{}) (bs []byte, err error) {
  184. err = NewEncoderBytes(&bs, testMsgpackH).Encode(ts)
  185. return
  186. }
  187. func fnMsgpackDecodeFn(buf []byte, ts interface{}) error {
  188. return NewDecoderBytes(buf, testMsgpackH).Decode(ts)
  189. }
  190. func fnBincEncodeFn(ts interface{}, sym AsSymbolFlag) (bs []byte, err error) {
  191. tSym := testBincH.AsSymbols
  192. testBincH.AsSymbols = sym
  193. err = NewEncoderBytes(&bs, testBincH).Encode(ts)
  194. testBincH.AsSymbols = tSym
  195. return
  196. }
  197. func fnBincDecodeFn(buf []byte, ts interface{}, sym AsSymbolFlag) (err error) {
  198. tSym := testBincH.AsSymbols
  199. testBincH.AsSymbols = sym
  200. err = NewDecoderBytes(buf, testBincH).Decode(ts)
  201. testBincH.AsSymbols = tSym
  202. return
  203. }
  204. func fnBincNoSymEncodeFn(ts interface{}) (bs []byte, err error) {
  205. return fnBincEncodeFn(ts, AsSymbolNone)
  206. }
  207. func fnBincNoSymDecodeFn(buf []byte, ts interface{}) error {
  208. return fnBincDecodeFn(buf, ts, AsSymbolNone)
  209. }
  210. func fnBincSymEncodeFn(ts interface{}) (bs []byte, err error) {
  211. return fnBincEncodeFn(ts, AsSymbolAll)
  212. }
  213. func fnBincSymDecodeFn(buf []byte, ts interface{}) error {
  214. return fnBincDecodeFn(buf, ts, AsSymbolAll)
  215. }
  216. func fnSimpleEncodeFn(ts interface{}) (bs []byte, err error) {
  217. err = NewEncoderBytes(&bs, testSimpleH).Encode(ts)
  218. return
  219. }
  220. func fnSimpleDecodeFn(buf []byte, ts interface{}) error {
  221. return NewDecoderBytes(buf, testSimpleH).Decode(ts)
  222. }
  223. func fnGobEncodeFn(ts interface{}) ([]byte, error) {
  224. bbuf := new(bytes.Buffer)
  225. err := gob.NewEncoder(bbuf).Encode(ts)
  226. return bbuf.Bytes(), err
  227. }
  228. func fnGobDecodeFn(buf []byte, ts interface{}) error {
  229. return gob.NewDecoder(bytes.NewBuffer(buf)).Decode(ts)
  230. }
  231. func fnJsonEncodeFn(ts interface{}) ([]byte, error) {
  232. return json.Marshal(ts)
  233. }
  234. func fnJsonDecodeFn(buf []byte, ts interface{}) error {
  235. return json.Unmarshal(buf, ts)
  236. }
  237. func Benchmark__Msgpack____Encode(b *testing.B) {
  238. fnBenchmarkEncode(b, "msgpack", benchTs, fnMsgpackEncodeFn)
  239. }
  240. func Benchmark__Msgpack____Decode(b *testing.B) {
  241. fnBenchmarkDecode(b, "msgpack", benchTs, fnMsgpackEncodeFn, fnMsgpackDecodeFn, fnBenchNewTs)
  242. }
  243. func Benchmark__Binc_NoSym_Encode(b *testing.B) {
  244. fnBenchmarkEncode(b, "binc", benchTs, fnBincNoSymEncodeFn)
  245. }
  246. func Benchmark__Binc_NoSym_Decode(b *testing.B) {
  247. fnBenchmarkDecode(b, "binc", benchTs, fnBincNoSymEncodeFn, fnBincNoSymDecodeFn, fnBenchNewTs)
  248. }
  249. func Benchmark__Binc_Sym___Encode(b *testing.B) {
  250. fnBenchmarkEncode(b, "binc", benchTs, fnBincSymEncodeFn)
  251. }
  252. func Benchmark__Binc_Sym___Decode(b *testing.B) {
  253. fnBenchmarkDecode(b, "binc", benchTs, fnBincSymEncodeFn, fnBincSymDecodeFn, fnBenchNewTs)
  254. }
  255. func Benchmark__Simple_____Encode(b *testing.B) {
  256. fnBenchmarkEncode(b, "simple", benchTs, fnSimpleEncodeFn)
  257. }
  258. func Benchmark__Simple_____Decode(b *testing.B) {
  259. fnBenchmarkDecode(b, "simple", benchTs, fnSimpleEncodeFn, fnSimpleDecodeFn, fnBenchNewTs)
  260. }
  261. func Benchmark__Gob________Encode(b *testing.B) {
  262. fnBenchmarkEncode(b, "gob", benchTs, fnGobEncodeFn)
  263. }
  264. func Benchmark__Gob________Decode(b *testing.B) {
  265. fnBenchmarkDecode(b, "gob", benchTs, fnGobEncodeFn, fnGobDecodeFn, fnBenchNewTs)
  266. }
  267. func Benchmark__Json_______Encode(b *testing.B) {
  268. fnBenchmarkEncode(b, "json", benchTs, fnJsonEncodeFn)
  269. }
  270. func Benchmark__Json_______Decode(b *testing.B) {
  271. fnBenchmarkDecode(b, "json", benchTs, fnJsonEncodeFn, fnJsonDecodeFn, fnBenchNewTs)
  272. }