bench_test.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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 Msgpack__Encode
  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. benchInitChan = make(chan bool, 1)
  29. benchCheckers []benchChecker
  30. )
  31. type benchEncFn func(*TestStruc) ([]byte, error)
  32. type benchDecFn func([]byte, *TestStruc) error
  33. type benchChecker struct {
  34. name string
  35. encodefn benchEncFn
  36. decodefn benchDecFn
  37. }
  38. func init() {
  39. flag.BoolVar(&benchInitDebug, "bdbg", 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. flag.Parse()
  45. benchTs = newTestStruc(benchDepth, true)
  46. approxSize = approxDataSize(reflect.ValueOf(benchTs))
  47. bytesLen := 1024 * 4 * (benchDepth + 1) * (benchDepth + 1)
  48. if bytesLen < approxSize {
  49. bytesLen = approxSize
  50. }
  51. benchCheckers = append(benchCheckers,
  52. benchChecker{"msgpack", fnMsgpackEncodeFn, fnMsgpackDecodeFn},
  53. benchChecker{"binc", fnBincEncodeFn, fnBincDecodeFn},
  54. benchChecker{"gob", fnGobEncodeFn, fnGobDecodeFn},
  55. benchChecker{"json", fnJsonEncodeFn, fnJsonDecodeFn},
  56. )
  57. if benchDoInitBench {
  58. go func() {
  59. <-benchInitChan
  60. runBenchInit()
  61. }()
  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, 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 doBenchCheck(name string, encfn benchEncFn, decfn benchDecFn) {
  88. runtime.GC()
  89. tnow := time.Now()
  90. buf, err := encfn(benchTs)
  91. if err != nil {
  92. logT(nil, "\t%10s: **** Error encoding benchTs: %v", name, err)
  93. }
  94. encDur := time.Now().Sub(tnow)
  95. encLen := len(buf)
  96. runtime.GC()
  97. if !benchUnscientificRes {
  98. logT(nil, "\t%10s: len: %d bytes\n", name, encLen)
  99. return
  100. }
  101. tnow = time.Now()
  102. if err = decfn(buf, new(TestStruc)); err != nil {
  103. logT(nil, "\t%10s: **** Error decoding into new TestStruc: %v", name, err)
  104. }
  105. decDur := time.Now().Sub(tnow)
  106. logT(nil, "\t%10s: len: %d bytes, encode: %v, decode: %v\n", name, encLen, encDur, decDur)
  107. }
  108. func fnBenchmarkEncode(b *testing.B, encName string, encfn benchEncFn) {
  109. runtime.GC()
  110. b.ResetTimer()
  111. for i := 0; i < b.N; i++ {
  112. _, err := encfn(benchTs)
  113. if err != nil {
  114. logT(b, "Error encoding benchTs: %s: %v", encName, err)
  115. b.FailNow()
  116. }
  117. }
  118. }
  119. func fnBenchmarkDecode(b *testing.B, encName string, encfn benchEncFn, decfn benchDecFn) {
  120. buf, err := encfn(benchTs)
  121. if err != nil {
  122. logT(b, "Error encoding benchTs: %s: %v", encName, err)
  123. b.FailNow()
  124. }
  125. runtime.GC()
  126. b.ResetTimer()
  127. for i := 0; i < b.N; i++ {
  128. ts := new(TestStruc)
  129. if err = decfn(buf, ts); err != nil {
  130. logT(b, "Error decoding into new TestStruc: %s: %v", encName, err)
  131. b.FailNow()
  132. }
  133. if benchVerify {
  134. verifyTsTree(b, ts)
  135. }
  136. }
  137. }
  138. func verifyTsTree(b *testing.B, ts *TestStruc) {
  139. var ts0, ts1m, ts2m, ts1s, ts2s *TestStruc
  140. ts0 = ts
  141. if benchDepth > 0 {
  142. ts1m, ts1s = verifyCheckAndGet(b, ts0)
  143. }
  144. if benchDepth > 1 {
  145. ts2m, ts2s = verifyCheckAndGet(b, ts1m)
  146. }
  147. for _, tsx := range []*TestStruc{ts0, ts1m, ts2m, ts1s, ts2s} {
  148. if tsx != nil {
  149. verifyOneOne(b, tsx)
  150. }
  151. }
  152. }
  153. func verifyCheckAndGet(b *testing.B, ts0 *TestStruc) (ts1m *TestStruc, ts1s *TestStruc) {
  154. // if len(ts1m.Ms) <= 2 {
  155. // logT(b, "Error: ts1m.Ms len should be > 2. Got: %v", len(ts1m.Ms))
  156. // b.FailNow()
  157. // }
  158. if len(ts0.Its) == 0 {
  159. logT(b, "Error: ts0.Islice len should be > 0. Got: %v", len(ts0.Its))
  160. b.FailNow()
  161. }
  162. ts1m = ts0.Mtsptr["0"]
  163. ts1s = ts0.Its[0]
  164. if ts1m == nil || ts1s == nil {
  165. logT(b, "Error: At benchDepth 1, No *TestStruc found")
  166. b.FailNow()
  167. }
  168. return
  169. }
  170. func verifyOneOne(b *testing.B, ts *TestStruc) {
  171. if ts.I64slice[2] != int64(3) {
  172. logT(b, "Error: Decode failed by checking values")
  173. b.FailNow()
  174. }
  175. }
  176. func fnMsgpackEncodeFn(ts *TestStruc) (bs []byte, err error) {
  177. err = NewEncoderBytes(&bs, testMsgpackH).Encode(ts)
  178. return
  179. }
  180. func fnMsgpackDecodeFn(buf []byte, ts *TestStruc) error {
  181. return NewDecoderBytes(buf, testMsgpackH).Decode(ts)
  182. }
  183. func fnBincEncodeFn(ts *TestStruc) (bs []byte, err error) {
  184. err = NewEncoderBytes(&bs, testBincH).Encode(ts)
  185. return
  186. }
  187. func fnBincDecodeFn(buf []byte, ts *TestStruc) error {
  188. return NewDecoderBytes(buf, testBincH).Decode(ts)
  189. }
  190. func fnGobEncodeFn(ts *TestStruc) ([]byte, error) {
  191. bbuf := new(bytes.Buffer)
  192. err := gob.NewEncoder(bbuf).Encode(ts)
  193. return bbuf.Bytes(), err
  194. }
  195. func fnGobDecodeFn(buf []byte, ts *TestStruc) error {
  196. return gob.NewDecoder(bytes.NewBuffer(buf)).Decode(ts)
  197. }
  198. func fnJsonEncodeFn(ts *TestStruc) ([]byte, error) {
  199. return json.Marshal(ts)
  200. }
  201. func fnJsonDecodeFn(buf []byte, ts *TestStruc) error {
  202. return json.Unmarshal(buf, ts)
  203. }
  204. func Benchmark__Msgpack__Encode(b *testing.B) {
  205. fnBenchmarkEncode(b, "msgpack", fnMsgpackEncodeFn)
  206. }
  207. func Benchmark__Msgpack__Decode(b *testing.B) {
  208. fnBenchmarkDecode(b, "msgpack", fnMsgpackEncodeFn, fnMsgpackDecodeFn)
  209. }
  210. func Benchmark__Binc_____Encode(b *testing.B) {
  211. fnBenchmarkEncode(b, "binc", fnBincEncodeFn)
  212. }
  213. func Benchmark__Binc_____Decode(b *testing.B) {
  214. fnBenchmarkDecode(b, "binc", fnBincEncodeFn, fnBincDecodeFn)
  215. }
  216. func Benchmark__Gob______Encode(b *testing.B) {
  217. fnBenchmarkEncode(b, "gob", fnGobEncodeFn)
  218. }
  219. func Benchmark__Gob______Decode(b *testing.B) {
  220. fnBenchmarkDecode(b, "gob", fnGobEncodeFn, fnGobDecodeFn)
  221. }
  222. func Benchmark__Json_____Encode(b *testing.B) {
  223. fnBenchmarkEncode(b, "json", fnJsonEncodeFn)
  224. }
  225. func Benchmark__Json_____Decode(b *testing.B) {
  226. fnBenchmarkDecode(b, "json", fnJsonEncodeFn, fnJsonDecodeFn)
  227. }