bench_test.go 9.1 KB

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