bench_test.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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. // bench_test is the "helper" file for all benchmarking tests.
  5. //
  6. // There are also benchmarks which depend on just codec and the stdlib,
  7. // and benchmarks which depend on external libraries.
  8. // It is an explicit goal that you can run benchmarks without external
  9. // dependencies (which is why the 'x' build tag was explicitly introduced).
  10. //
  11. // There are 2 ways of running tests:
  12. // - generated
  13. // - not generated
  14. //
  15. // Consequently, we have 4 groups:
  16. // - codec_bench (gen, !gen)
  17. // - stdlib_bench (!gen only)
  18. // - x_bench (!gen only)
  19. // - x_bench_gen (gen only)
  20. //
  21. // We also have 4 matching suite files.
  22. // - z_all_bench (rename later to z_all_codec_bench???)
  23. // - z_all_stdlib_bench
  24. // - z_all_x_bench
  25. // - z_all_x_bench_gen
  26. //
  27. // Finally, we have a single test (TestBenchInit) that
  28. // will log information about whether each format can
  29. // encode or not, how long to encode (unscientifically),
  30. // and the encode size.
  31. //
  32. // This test MUST be run always, as it calls init() internally
  33. import (
  34. "reflect"
  35. "runtime"
  36. "testing"
  37. "time"
  38. )
  39. // Sample way to run:
  40. // go test -bi -bv -bd=1 -benchmem -bench=.
  41. const (
  42. benchUnscientificRes = true
  43. benchVerify = true
  44. benchRecover = true
  45. benchShowJsonOnError = true
  46. )
  47. var (
  48. benchTs *TestStruc
  49. approxSize int
  50. benchCheckers []benchChecker
  51. )
  52. type benchEncFn func(interface{}, []byte) ([]byte, error)
  53. type benchDecFn func([]byte, interface{}) error
  54. type benchIntfFn func() interface{}
  55. type benchChecker struct {
  56. name string
  57. encodefn benchEncFn
  58. decodefn benchDecFn
  59. }
  60. func init() {
  61. testPreInitFns = append(testPreInitFns, benchPreInit)
  62. // testPostInitFns = append(testPostInitFns, codecbenchPostInit)
  63. }
  64. func benchPreInit() {
  65. benchTs = newTestStruc(testDepth, testNumRepeatString, true, !testSkipIntf, testMapStringKeyOnly)
  66. approxSize = approxDataSize(reflect.ValueOf(benchTs)) * 3 / 2 // multiply by 1.5 to appease msgp, and prevent alloc
  67. // bytesLen := 1024 * 4 * (testDepth + 1) * (testDepth + 1)
  68. // if bytesLen < approxSize {
  69. // bytesLen = approxSize
  70. // }
  71. }
  72. func benchReinit() {
  73. benchCheckers = nil
  74. }
  75. func benchmarkDivider() {
  76. // logTv(nil, "-------------------------------\n")
  77. println()
  78. }
  79. // func Test0(t *testing.T) {
  80. // testOnce.Do(testInitAll)
  81. // }
  82. func TestBenchInit(t *testing.T) {
  83. testOnce.Do(testInitAll)
  84. if !testing.Verbose() {
  85. return
  86. }
  87. // t.Logf("..............................................")
  88. t.Logf("BENCHMARK INIT: %v", time.Now())
  89. // t.Logf("To run full benchmark comparing encodings, use: \"go test -bench=.\"")
  90. t.Logf("Benchmark: ")
  91. t.Logf("\tStruct recursive Depth: %d", testDepth)
  92. if approxSize > 0 {
  93. t.Logf("\tApproxDeepSize Of benchmark Struct: %d bytes", approxSize)
  94. }
  95. if benchUnscientificRes {
  96. t.Logf("Benchmark One-Pass Run (with Unscientific Encode/Decode times): ")
  97. } else {
  98. t.Logf("Benchmark One-Pass Run:")
  99. }
  100. for _, bc := range benchCheckers {
  101. doBenchCheck(t, bc.name, bc.encodefn, bc.decodefn)
  102. }
  103. if testVerbose {
  104. t.Logf("..............................................")
  105. t.Logf("<<<<====>>>> depth: %v, ts: %#v\n", testDepth, benchTs)
  106. }
  107. runtime.GC()
  108. time.Sleep(100 * time.Millisecond)
  109. }
  110. var vBenchTs = TestStruc{}
  111. func fnBenchNewTs() interface{} {
  112. vBenchTs = TestStruc{}
  113. return &vBenchTs
  114. // return new(TestStruc)
  115. }
  116. // const benchCheckDoDeepEqual = false
  117. func benchRecoverPanic(t *testing.B) {
  118. if benchRecover {
  119. if r := recover(); r != nil {
  120. t.Logf("(recovered) panic: %v\n", r)
  121. }
  122. }
  123. }
  124. func benchRecoverPanicT(t *testing.T) {
  125. if benchRecover {
  126. if r := recover(); r != nil {
  127. t.Logf("(recovered) panic: %v\n", r)
  128. }
  129. }
  130. }
  131. func doBenchCheck(t *testing.T, name string, encfn benchEncFn, decfn benchDecFn) {
  132. // if benchUnscientificRes {
  133. // t.Logf("-------------- %s ----------------", name)
  134. // }
  135. defer benchRecoverPanicT(t)
  136. runtime.GC()
  137. tnow := time.Now()
  138. buf, err := encfn(benchTs, nil)
  139. if err != nil {
  140. t.Logf("\t%10s: **** Error encoding benchTs: %v", name, err)
  141. return
  142. }
  143. encDur := time.Since(tnow)
  144. encLen := len(buf)
  145. runtime.GC()
  146. if !benchUnscientificRes {
  147. t.Logf("\t%10s: len: %d bytes\n", name, encLen)
  148. return
  149. }
  150. tnow = time.Now()
  151. var ts2 TestStruc
  152. if err = decfn(buf, &ts2); err != nil {
  153. t.Logf("\t%10s: **** Error decoding into new TestStruc: %v", name, err)
  154. return
  155. }
  156. decDur := time.Since(tnow)
  157. // if benchCheckDoDeepEqual {
  158. if benchVerify {
  159. err = deepEqual(benchTs, &ts2)
  160. if err == nil {
  161. t.Logf("\t%10s: len: %d bytes,\t encode: %v,\t decode: %v,\tencoded == decoded", name, encLen, encDur, decDur)
  162. } else {
  163. t.Logf("\t%10s: len: %d bytes,\t encode: %v,\t decode: %v,\tencoded != decoded: %v", name, encLen, encDur, decDur, err)
  164. // if benchShowJsonOnError && strings.Contains(name, "json") {
  165. // fmt.Printf("\n\n%s\n\n", buf)
  166. // //fmt.Printf("\n\n%#v\n\n", benchTs)
  167. // //fmt.Printf("\n\n%#v\n\n", &ts2)
  168. // return
  169. // }
  170. // if strings.Contains(name, "json") {
  171. // println(">>>>>")
  172. // f1, _ := os.Create("1.out")
  173. // f2, _ := os.Create("2.out")
  174. // f3, _ := os.Create("3.json")
  175. // buf3, _ := json.MarshalIndent(&ts2, "", "\t")
  176. // spew.Config.SortKeys = true
  177. // spew.Config.SpewKeys = true
  178. // println("^^^^^^^^^^^^^^")
  179. // spew.Fdump(f1, benchTs)
  180. // println("^^^^^^^^^^^^^^")
  181. // spew.Fdump(f2, &ts2)
  182. // println("^^^^^^^^^^^^^^")
  183. // f3.Write(buf3)
  184. // f1.Close()
  185. // f2.Close()
  186. // f3.Close()
  187. // }
  188. // t.Logf("\t: err: %v,\n benchTs: %#v\n\n, ts2: %#v\n\n", err, benchTs, ts2) // TODO: remove
  189. // t.Logf("BenchVerify: Error comparing en|decoded TestStruc: %v", err)
  190. // return
  191. // t.Logf("BenchVerify: Error comparing benchTs: %v\n--------\n%v\n--------\n%v", err, benchTs, ts2)
  192. // if strings.Contains(name, "json") {
  193. // t.Logf("\n\tDECODED FROM\n--------\n%s", buf)
  194. // }
  195. }
  196. } else {
  197. t.Logf("\t%10s: len: %d bytes,\t encode: %v,\t decode: %v", name, encLen, encDur, decDur)
  198. }
  199. return
  200. }
  201. func fnBenchmarkEncode(b *testing.B, encName string, ts interface{}, encfn benchEncFn) {
  202. defer benchRecoverPanic(b)
  203. testOnce.Do(testInitAll)
  204. var err error
  205. bs := make([]byte, 0, approxSize)
  206. runtime.GC()
  207. b.ResetTimer()
  208. for i := 0; i < b.N; i++ {
  209. if _, err = encfn(ts, bs); err != nil {
  210. break
  211. }
  212. }
  213. if err != nil {
  214. b.Logf("Error encoding benchTs: %s: %v", encName, err)
  215. b.FailNow()
  216. }
  217. }
  218. func fnBenchmarkDecode(b *testing.B, encName string, ts interface{},
  219. encfn benchEncFn, decfn benchDecFn, newfn benchIntfFn,
  220. ) {
  221. defer benchRecoverPanic(b)
  222. testOnce.Do(testInitAll)
  223. bs := make([]byte, 0, approxSize)
  224. buf, err := encfn(ts, bs)
  225. if err != nil {
  226. b.Logf("Error encoding benchTs: %s: %v", encName, err)
  227. b.FailNow()
  228. }
  229. // if false && benchVerify { // do not do benchVerify during decode
  230. // // ts2 := newfn()
  231. // ts1 := ts.(*TestStruc)
  232. // ts2 := new(TestStruc)
  233. // if err = decfn(buf, ts2); err != nil {
  234. // failT(b, "BenchVerify: Error decoding benchTs: %s: %v", encName, err)
  235. // }
  236. // if err = deepEqual(ts1, ts2); err != nil {
  237. // failT(b, "BenchVerify: Error comparing benchTs: %s: %v", encName, err)
  238. // }
  239. // }
  240. runtime.GC()
  241. b.ResetTimer()
  242. for i := 0; i < b.N; i++ {
  243. ts = newfn()
  244. if err = decfn(buf, ts); err != nil {
  245. break
  246. }
  247. }
  248. if err != nil {
  249. b.Logf("Error decoding into new TestStruc: %s: %v", encName, err)
  250. b.FailNow()
  251. }
  252. }