bench_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. // Copyright (c) 2012-2018 Ugorji Nwoke. All rights reserved.
  2. // Use of this source code is governed by a MIT license found in the LICENSE file.
  3. package codec
  4. import (
  5. "bytes"
  6. "encoding/gob"
  7. "encoding/json"
  8. "encoding/xml"
  9. "reflect"
  10. "runtime"
  11. "testing"
  12. "time"
  13. )
  14. // Sample way to run:
  15. // go test -bi -bv -bd=1 -benchmem -bench=.
  16. func init() {
  17. testPreInitFns = append(testPreInitFns, benchPreInit)
  18. testPostInitFns = append(testPostInitFns, benchPostInit)
  19. }
  20. var (
  21. benchTs *TestStruc
  22. approxSize int
  23. benchCheckers []benchChecker
  24. )
  25. type benchEncFn func(interface{}, []byte) ([]byte, error)
  26. type benchDecFn func([]byte, interface{}) error
  27. type benchIntfFn func() interface{}
  28. type benchChecker struct {
  29. name string
  30. encodefn benchEncFn
  31. decodefn benchDecFn
  32. }
  33. func benchReinit() {
  34. benchCheckers = nil
  35. }
  36. func benchPreInit() {
  37. benchTs = newTestStruc(benchDepth, testNumRepeatString, true, !testSkipIntf, benchMapStringKeyOnly)
  38. approxSize = approxDataSize(reflect.ValueOf(benchTs)) * 3 / 2 // multiply by 1.5 to appease msgp, and prevent alloc
  39. // bytesLen := 1024 * 4 * (benchDepth + 1) * (benchDepth + 1)
  40. // if bytesLen < approxSize {
  41. // bytesLen = approxSize
  42. // }
  43. benchCheckers = append(benchCheckers,
  44. benchChecker{"msgpack", fnMsgpackEncodeFn, fnMsgpackDecodeFn},
  45. benchChecker{"binc", fnBincEncodeFn, fnBincDecodeFn},
  46. benchChecker{"simple", fnSimpleEncodeFn, fnSimpleDecodeFn},
  47. benchChecker{"cbor", fnCborEncodeFn, fnCborDecodeFn},
  48. benchChecker{"json", fnJsonEncodeFn, fnJsonDecodeFn},
  49. benchChecker{"std-json", fnStdJsonEncodeFn, fnStdJsonDecodeFn},
  50. benchChecker{"gob", fnGobEncodeFn, fnGobDecodeFn},
  51. benchChecker{"std-xml", fnStdXmlEncodeFn, fnStdXmlDecodeFn},
  52. )
  53. }
  54. func benchPostInit() {
  55. if benchDoInitBench {
  56. runBenchInit()
  57. }
  58. }
  59. func runBenchInit() {
  60. // logTv(nil, "..............................................")
  61. logTv(nil, "BENCHMARK INIT: %v", time.Now())
  62. // logTv(nil, "To run full benchmark comparing encodings, use: \"go test -bench=.\"")
  63. logTv(nil, "Benchmark: ")
  64. logTv(nil, "\tStruct recursive Depth: %d", benchDepth)
  65. if approxSize > 0 {
  66. logTv(nil, "\tApproxDeepSize Of benchmark Struct: %d bytes", approxSize)
  67. }
  68. if benchUnscientificRes {
  69. logTv(nil, "Benchmark One-Pass Run (with Unscientific Encode/Decode times): ")
  70. } else {
  71. logTv(nil, "Benchmark One-Pass Run:")
  72. }
  73. for _, bc := range benchCheckers {
  74. doBenchCheck(bc.name, bc.encodefn, bc.decodefn)
  75. }
  76. logTv(nil, "..............................................")
  77. if benchInitDebug {
  78. logTv(nil, "<<<<====>>>> depth: %v, ts: %#v\n", benchDepth, benchTs)
  79. }
  80. runtime.GC()
  81. time.Sleep(100 * time.Millisecond)
  82. }
  83. var vBenchTs = TestStruc{}
  84. func fnBenchNewTs() interface{} {
  85. vBenchTs = TestStruc{}
  86. return &vBenchTs
  87. // return new(TestStruc)
  88. }
  89. // const benchCheckDoDeepEqual = false
  90. func benchRecoverPanic(t interface{}) {
  91. if r := recover(); r != nil {
  92. logT(t, "(recovered) panic: %v\n", r)
  93. }
  94. }
  95. func doBenchCheck(name string, encfn benchEncFn, decfn benchDecFn) {
  96. // if benchUnscientificRes {
  97. // logTv(nil, "-------------- %s ----------------", name)
  98. // }
  99. defer benchRecoverPanic(nil)
  100. runtime.GC()
  101. tnow := time.Now()
  102. buf, err := encfn(benchTs, nil)
  103. if err != nil {
  104. logTv(nil, "\t%10s: **** Error encoding benchTs: %v", name, err)
  105. return
  106. }
  107. encDur := time.Since(tnow)
  108. encLen := len(buf)
  109. runtime.GC()
  110. if !benchUnscientificRes {
  111. logTv(nil, "\t%10s: len: %d bytes\n", name, encLen)
  112. return
  113. }
  114. tnow = time.Now()
  115. var ts2 TestStruc
  116. if err = decfn(buf, &ts2); err != nil {
  117. logTv(nil, "\t%10s: **** Error decoding into new TestStruc: %v", name, err)
  118. return
  119. }
  120. decDur := time.Since(tnow)
  121. // if benchCheckDoDeepEqual {
  122. if benchVerify {
  123. err = deepEqual(benchTs, &ts2)
  124. if err == nil {
  125. logTv(nil, "\t%10s: len: %d bytes,\t encode: %v,\t decode: %v,\tencoded = decoded", name, encLen, encDur, decDur)
  126. } else {
  127. logTv(nil, "\t%10s: len: %d bytes,\t encode: %v,\t decode: %v,\tencoded != decoded: %v", name, encLen, encDur, decDur, err)
  128. // if strings.Contains(name, "json") {
  129. // println(">>>>>")
  130. // f1, _ := os.Create("1.out")
  131. // f2, _ := os.Create("2.out")
  132. // f3, _ := os.Create("3.json")
  133. // buf3, _ := json.MarshalIndent(&ts2, "", "\t")
  134. // spew.Config.SortKeys = true
  135. // spew.Config.SpewKeys = true
  136. // println("^^^^^^^^^^^^^^")
  137. // spew.Fdump(f1, benchTs)
  138. // println("^^^^^^^^^^^^^^")
  139. // spew.Fdump(f2, &ts2)
  140. // println("^^^^^^^^^^^^^^")
  141. // f3.Write(buf3)
  142. // f1.Close()
  143. // f2.Close()
  144. // f3.Close()
  145. // }
  146. // logTv(nil, "\t: err: %v,\n benchTs: %#v\n\n, ts2: %#v\n\n", err, benchTs, ts2) // TODO: remove
  147. // logTv(nil, "BenchVerify: Error comparing en|decoded TestStruc: %v", err)
  148. // return
  149. // logTv(nil, "BenchVerify: Error comparing benchTs: %v\n--------\n%v\n--------\n%v", err, benchTs, ts2)
  150. // if strings.Contains(name, "json") {
  151. // logTv(nil, "\n\tDECODED FROM\n--------\n%s", buf)
  152. // }
  153. }
  154. } else {
  155. logTv(nil, "\t%10s: len: %d bytes,\t encode: %v,\t decode: %v", name, encLen, encDur, decDur)
  156. }
  157. return
  158. }
  159. func fnBenchmarkEncode(b *testing.B, encName string, ts interface{}, encfn benchEncFn) {
  160. defer benchRecoverPanic(b)
  161. testOnce.Do(testInitAll)
  162. var err error
  163. bs := make([]byte, 0, approxSize)
  164. runtime.GC()
  165. b.ResetTimer()
  166. for i := 0; i < b.N; i++ {
  167. if _, err = encfn(ts, bs); err != nil {
  168. break
  169. }
  170. }
  171. if err != nil {
  172. failT(b, "Error encoding benchTs: %s: %v", encName, err)
  173. }
  174. }
  175. func fnBenchmarkDecode(b *testing.B, encName string, ts interface{},
  176. encfn benchEncFn, decfn benchDecFn, newfn benchIntfFn,
  177. ) {
  178. defer benchRecoverPanic(b)
  179. testOnce.Do(testInitAll)
  180. bs := make([]byte, 0, approxSize)
  181. buf, err := encfn(ts, bs)
  182. if err != nil {
  183. failT(b, "Error encoding benchTs: %s: %v", encName, err)
  184. }
  185. if false && benchVerify { // do not do benchVerify during decode
  186. // ts2 := newfn()
  187. ts1 := ts.(*TestStruc)
  188. ts2 := new(TestStruc)
  189. if err = decfn(buf, ts2); err != nil {
  190. failT(b, "BenchVerify: Error decoding benchTs: %s: %v", encName, err)
  191. }
  192. if err = deepEqual(ts1, ts2); err != nil {
  193. failT(b, "BenchVerify: Error comparing benchTs: %s: %v", encName, err)
  194. }
  195. }
  196. runtime.GC()
  197. b.ResetTimer()
  198. for i := 0; i < b.N; i++ {
  199. ts = newfn()
  200. if err = decfn(buf, ts); err != nil {
  201. break
  202. }
  203. }
  204. if err != nil {
  205. failT(b, "Error decoding into new TestStruc: %s: %v", encName, err)
  206. }
  207. }
  208. // ------------ tests below
  209. func fnMsgpackEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
  210. return sTestCodecEncode(ts, bsIn, fnBenchmarkByteBuf, testMsgpackH, &testMsgpackH.BasicHandle)
  211. }
  212. func fnMsgpackDecodeFn(buf []byte, ts interface{}) error {
  213. return sTestCodecDecode(buf, ts, testMsgpackH, &testMsgpackH.BasicHandle)
  214. }
  215. func fnBincEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
  216. return sTestCodecEncode(ts, bsIn, fnBenchmarkByteBuf, testBincH, &testBincH.BasicHandle)
  217. }
  218. func fnBincDecodeFn(buf []byte, ts interface{}) error {
  219. return sTestCodecDecode(buf, ts, testBincH, &testBincH.BasicHandle)
  220. }
  221. func fnSimpleEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
  222. return sTestCodecEncode(ts, bsIn, fnBenchmarkByteBuf, testSimpleH, &testSimpleH.BasicHandle)
  223. }
  224. func fnSimpleDecodeFn(buf []byte, ts interface{}) error {
  225. return sTestCodecDecode(buf, ts, testSimpleH, &testSimpleH.BasicHandle)
  226. }
  227. func fnCborEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
  228. return sTestCodecEncode(ts, bsIn, fnBenchmarkByteBuf, testCborH, &testCborH.BasicHandle)
  229. }
  230. func fnCborDecodeFn(buf []byte, ts interface{}) error {
  231. return sTestCodecDecode(buf, ts, testCborH, &testCborH.BasicHandle)
  232. }
  233. func fnJsonEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
  234. return sTestCodecEncode(ts, bsIn, fnBenchmarkByteBuf, testJsonH, &testJsonH.BasicHandle)
  235. }
  236. func fnJsonDecodeFn(buf []byte, ts interface{}) error {
  237. return sTestCodecDecode(buf, ts, testJsonH, &testJsonH.BasicHandle)
  238. }
  239. func fnGobEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
  240. buf := fnBenchmarkByteBuf(bsIn)
  241. err := gob.NewEncoder(buf).Encode(ts)
  242. return buf.Bytes(), err
  243. }
  244. func fnGobDecodeFn(buf []byte, ts interface{}) error {
  245. return gob.NewDecoder(bytes.NewReader(buf)).Decode(ts)
  246. }
  247. func fnStdXmlEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
  248. buf := fnBenchmarkByteBuf(bsIn)
  249. err := xml.NewEncoder(buf).Encode(ts)
  250. return buf.Bytes(), err
  251. }
  252. func fnStdXmlDecodeFn(buf []byte, ts interface{}) error {
  253. return xml.NewDecoder(bytes.NewReader(buf)).Decode(ts)
  254. }
  255. func fnStdJsonEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
  256. if testUseIoEncDec >= 0 {
  257. buf := fnBenchmarkByteBuf(bsIn)
  258. err := json.NewEncoder(buf).Encode(ts)
  259. return buf.Bytes(), err
  260. }
  261. return json.Marshal(ts)
  262. }
  263. func fnStdJsonDecodeFn(buf []byte, ts interface{}) error {
  264. if testUseIoEncDec >= 0 {
  265. return json.NewDecoder(bytes.NewReader(buf)).Decode(ts)
  266. }
  267. return json.Unmarshal(buf, ts)
  268. }
  269. // ----------- DECODE ------------------
  270. func Benchmark__Msgpack____Encode(b *testing.B) {
  271. fnBenchmarkEncode(b, "msgpack", benchTs, fnMsgpackEncodeFn)
  272. }
  273. func Benchmark__Binc_______Encode(b *testing.B) {
  274. fnBenchmarkEncode(b, "binc", benchTs, fnBincEncodeFn)
  275. }
  276. func Benchmark__Simple_____Encode(b *testing.B) {
  277. fnBenchmarkEncode(b, "simple", benchTs, fnSimpleEncodeFn)
  278. }
  279. func Benchmark__Cbor_______Encode(b *testing.B) {
  280. fnBenchmarkEncode(b, "cbor", benchTs, fnCborEncodeFn)
  281. }
  282. func Benchmark__Json_______Encode(b *testing.B) {
  283. fnBenchmarkEncode(b, "json", benchTs, fnJsonEncodeFn)
  284. }
  285. func Benchmark__Std_Json___Encode(b *testing.B) {
  286. fnBenchmarkEncode(b, "std-json", benchTs, fnStdJsonEncodeFn)
  287. }
  288. func Benchmark__Gob________Encode(b *testing.B) {
  289. fnBenchmarkEncode(b, "gob", benchTs, fnGobEncodeFn)
  290. }
  291. func Benchmark__Std_Xml____Encode(b *testing.B) {
  292. fnBenchmarkEncode(b, "std-xml", benchTs, fnStdXmlEncodeFn)
  293. }
  294. // ----------- DECODE ------------------
  295. func Benchmark__Msgpack____Decode(b *testing.B) {
  296. fnBenchmarkDecode(b, "msgpack", benchTs, fnMsgpackEncodeFn, fnMsgpackDecodeFn, fnBenchNewTs)
  297. }
  298. func Benchmark__Binc_______Decode(b *testing.B) {
  299. fnBenchmarkDecode(b, "binc", benchTs, fnBincEncodeFn, fnBincDecodeFn, fnBenchNewTs)
  300. }
  301. func Benchmark__Simple_____Decode(b *testing.B) {
  302. fnBenchmarkDecode(b, "simple", benchTs, fnSimpleEncodeFn, fnSimpleDecodeFn, fnBenchNewTs)
  303. }
  304. func Benchmark__Cbor_______Decode(b *testing.B) {
  305. fnBenchmarkDecode(b, "cbor", benchTs, fnCborEncodeFn, fnCborDecodeFn, fnBenchNewTs)
  306. }
  307. func Benchmark__Json_______Decode(b *testing.B) {
  308. fnBenchmarkDecode(b, "json", benchTs, fnJsonEncodeFn, fnJsonDecodeFn, fnBenchNewTs)
  309. }
  310. func Benchmark__Std_Json___Decode(b *testing.B) {
  311. fnBenchmarkDecode(b, "std-json", benchTs, fnStdJsonEncodeFn, fnStdJsonDecodeFn, fnBenchNewTs)
  312. }
  313. func Benchmark__Gob________Decode(b *testing.B) {
  314. fnBenchmarkDecode(b, "gob", benchTs, fnGobEncodeFn, fnGobDecodeFn, fnBenchNewTs)
  315. }
  316. func Benchmark__Std_Xml____Decode(b *testing.B) {
  317. fnBenchmarkDecode(b, "std-xml", benchTs, fnStdXmlEncodeFn, fnStdXmlDecodeFn, fnBenchNewTs)
  318. }