bench_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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. // logT(nil, "..............................................")
  61. logT(nil, "BENCHMARK INIT: %v", time.Now())
  62. // logT(nil, "To run full benchmark comparing encodings, use: \"go test -bench=.\"")
  63. logT(nil, "Benchmark: ")
  64. logT(nil, "\tStruct recursive Depth: %d", benchDepth)
  65. if approxSize > 0 {
  66. logT(nil, "\tApproxDeepSize Of benchmark Struct: %d bytes", approxSize)
  67. }
  68. if benchUnscientificRes {
  69. logT(nil, "Benchmark One-Pass Run (with Unscientific Encode/Decode times): ")
  70. } else {
  71. logT(nil, "Benchmark One-Pass Run:")
  72. }
  73. for _, bc := range benchCheckers {
  74. doBenchCheck(bc.name, bc.encodefn, bc.decodefn)
  75. }
  76. logT(nil, "..............................................")
  77. if benchInitDebug {
  78. logT(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, "panic: %v\n", r)
  93. }
  94. }
  95. func doBenchCheck(name string, encfn benchEncFn, decfn benchDecFn) {
  96. // if benchUnscientificRes {
  97. // logT(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. logT(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. logT(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. logT(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. logT(nil, "\t%10s: len: %d bytes,\t encode: %v,\t decode: %v,\tencoded = decoded", name, encLen, encDur, decDur)
  126. } else {
  127. logT(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. // logT(nil, "\t: err: %v,\n benchTs: %#v\n\n, ts2: %#v\n\n", err, benchTs, ts2) // TODO: remove
  147. // logT(nil, "BenchVerify: Error comparing en|decoded TestStruc: %v", err)
  148. // return
  149. // logT(nil, "BenchVerify: Error comparing benchTs: %v\n--------\n%v\n--------\n%v", err, benchTs, ts2)
  150. // if strings.Contains(name, "json") {
  151. // logT(nil, "\n\tDECODED FROM\n--------\n%s", buf)
  152. // }
  153. }
  154. } else {
  155. logT(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. logT(b, "Error encoding benchTs: %s: %v", encName, err)
  173. b.FailNow()
  174. }
  175. }
  176. func fnBenchmarkDecode(b *testing.B, encName string, ts interface{},
  177. encfn benchEncFn, decfn benchDecFn, newfn benchIntfFn,
  178. ) {
  179. defer benchRecoverPanic(b)
  180. testOnce.Do(testInitAll)
  181. bs := make([]byte, 0, approxSize)
  182. buf, err := encfn(ts, bs)
  183. if err != nil {
  184. logT(b, "Error encoding benchTs: %s: %v", encName, err)
  185. b.FailNow()
  186. }
  187. if false && benchVerify { // do not do benchVerify during decode
  188. // ts2 := newfn()
  189. ts1 := ts.(*TestStruc)
  190. ts2 := new(TestStruc)
  191. if err = decfn(buf, ts2); err != nil {
  192. logT(b, "BenchVerify: Error decoding benchTs: %s: %v", encName, err)
  193. b.FailNow()
  194. }
  195. if err = deepEqual(ts1, ts2); err != nil {
  196. logT(b, "BenchVerify: Error comparing benchTs: %s: %v", encName, err)
  197. b.FailNow()
  198. }
  199. }
  200. runtime.GC()
  201. b.ResetTimer()
  202. for i := 0; i < b.N; i++ {
  203. ts = newfn()
  204. if err = decfn(buf, ts); err != nil {
  205. break
  206. }
  207. }
  208. if err != nil {
  209. logT(b, "Error decoding into new TestStruc: %s: %v", encName, err)
  210. b.FailNow()
  211. }
  212. }
  213. // ------------ tests below
  214. func fnMsgpackEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
  215. return sTestCodecEncode(ts, bsIn, fnBenchmarkByteBuf, testMsgpackH, &testMsgpackH.BasicHandle)
  216. }
  217. func fnMsgpackDecodeFn(buf []byte, ts interface{}) error {
  218. return sTestCodecDecode(buf, ts, testMsgpackH, &testMsgpackH.BasicHandle)
  219. }
  220. func fnBincEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
  221. return sTestCodecEncode(ts, bsIn, fnBenchmarkByteBuf, testBincH, &testBincH.BasicHandle)
  222. }
  223. func fnBincDecodeFn(buf []byte, ts interface{}) error {
  224. return sTestCodecDecode(buf, ts, testBincH, &testBincH.BasicHandle)
  225. }
  226. func fnSimpleEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
  227. return sTestCodecEncode(ts, bsIn, fnBenchmarkByteBuf, testSimpleH, &testSimpleH.BasicHandle)
  228. }
  229. func fnSimpleDecodeFn(buf []byte, ts interface{}) error {
  230. return sTestCodecDecode(buf, ts, testSimpleH, &testSimpleH.BasicHandle)
  231. }
  232. func fnCborEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
  233. return sTestCodecEncode(ts, bsIn, fnBenchmarkByteBuf, testCborH, &testCborH.BasicHandle)
  234. }
  235. func fnCborDecodeFn(buf []byte, ts interface{}) error {
  236. return sTestCodecDecode(buf, ts, testCborH, &testCborH.BasicHandle)
  237. }
  238. func fnJsonEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
  239. return sTestCodecEncode(ts, bsIn, fnBenchmarkByteBuf, testJsonH, &testJsonH.BasicHandle)
  240. }
  241. func fnJsonDecodeFn(buf []byte, ts interface{}) error {
  242. return sTestCodecDecode(buf, ts, testJsonH, &testJsonH.BasicHandle)
  243. }
  244. func fnGobEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
  245. buf := fnBenchmarkByteBuf(bsIn)
  246. err := gob.NewEncoder(buf).Encode(ts)
  247. return buf.Bytes(), err
  248. }
  249. func fnGobDecodeFn(buf []byte, ts interface{}) error {
  250. return gob.NewDecoder(bytes.NewReader(buf)).Decode(ts)
  251. }
  252. func fnStdXmlEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
  253. buf := fnBenchmarkByteBuf(bsIn)
  254. err := xml.NewEncoder(buf).Encode(ts)
  255. return buf.Bytes(), err
  256. }
  257. func fnStdXmlDecodeFn(buf []byte, ts interface{}) error {
  258. return xml.NewDecoder(bytes.NewReader(buf)).Decode(ts)
  259. }
  260. func fnStdJsonEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
  261. if testUseIoEncDec >= 0 {
  262. buf := fnBenchmarkByteBuf(bsIn)
  263. err := json.NewEncoder(buf).Encode(ts)
  264. return buf.Bytes(), err
  265. }
  266. return json.Marshal(ts)
  267. }
  268. func fnStdJsonDecodeFn(buf []byte, ts interface{}) error {
  269. if testUseIoEncDec >= 0 {
  270. return json.NewDecoder(bytes.NewReader(buf)).Decode(ts)
  271. }
  272. return json.Unmarshal(buf, ts)
  273. }
  274. // ----------- DECODE ------------------
  275. func Benchmark__Msgpack____Encode(b *testing.B) {
  276. fnBenchmarkEncode(b, "msgpack", benchTs, fnMsgpackEncodeFn)
  277. }
  278. func Benchmark__Binc_______Encode(b *testing.B) {
  279. fnBenchmarkEncode(b, "binc", benchTs, fnBincEncodeFn)
  280. }
  281. func Benchmark__Simple_____Encode(b *testing.B) {
  282. fnBenchmarkEncode(b, "simple", benchTs, fnSimpleEncodeFn)
  283. }
  284. func Benchmark__Cbor_______Encode(b *testing.B) {
  285. fnBenchmarkEncode(b, "cbor", benchTs, fnCborEncodeFn)
  286. }
  287. func Benchmark__Json_______Encode(b *testing.B) {
  288. fnBenchmarkEncode(b, "json", benchTs, fnJsonEncodeFn)
  289. }
  290. func Benchmark__Std_Json___Encode(b *testing.B) {
  291. fnBenchmarkEncode(b, "std-json", benchTs, fnStdJsonEncodeFn)
  292. }
  293. func Benchmark__Gob________Encode(b *testing.B) {
  294. fnBenchmarkEncode(b, "gob", benchTs, fnGobEncodeFn)
  295. }
  296. func Benchmark__Std_Xml____Encode(b *testing.B) {
  297. fnBenchmarkEncode(b, "std-xml", benchTs, fnStdXmlEncodeFn)
  298. }
  299. // ----------- DECODE ------------------
  300. func Benchmark__Msgpack____Decode(b *testing.B) {
  301. fnBenchmarkDecode(b, "msgpack", benchTs, fnMsgpackEncodeFn, fnMsgpackDecodeFn, fnBenchNewTs)
  302. }
  303. func Benchmark__Binc_______Decode(b *testing.B) {
  304. fnBenchmarkDecode(b, "binc", benchTs, fnBincEncodeFn, fnBincDecodeFn, fnBenchNewTs)
  305. }
  306. func Benchmark__Simple_____Decode(b *testing.B) {
  307. fnBenchmarkDecode(b, "simple", benchTs, fnSimpleEncodeFn, fnSimpleDecodeFn, fnBenchNewTs)
  308. }
  309. func Benchmark__Cbor_______Decode(b *testing.B) {
  310. fnBenchmarkDecode(b, "cbor", benchTs, fnCborEncodeFn, fnCborDecodeFn, fnBenchNewTs)
  311. }
  312. func Benchmark__Json_______Decode(b *testing.B) {
  313. fnBenchmarkDecode(b, "json", benchTs, fnJsonEncodeFn, fnJsonDecodeFn, fnBenchNewTs)
  314. }
  315. func Benchmark__Std_Json___Decode(b *testing.B) {
  316. fnBenchmarkDecode(b, "std-json", benchTs, fnStdJsonEncodeFn, fnStdJsonDecodeFn, fnBenchNewTs)
  317. }
  318. func Benchmark__Gob________Decode(b *testing.B) {
  319. fnBenchmarkDecode(b, "gob", benchTs, fnGobEncodeFn, fnGobDecodeFn, fnBenchNewTs)
  320. }
  321. func Benchmark__Std_Xml____Decode(b *testing.B) {
  322. fnBenchmarkDecode(b, "std-xml", benchTs, fnStdXmlEncodeFn, fnStdXmlDecodeFn, fnBenchNewTs)
  323. }