decode_test.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. // Go support for Protocol Buffers - Google's data interchange format
  2. //
  3. // Copyright 2010 The Go Authors. All rights reserved.
  4. // https://github.com/golang/protobuf
  5. //
  6. // Redistribution and use in source and binary forms, with or without
  7. // modification, are permitted provided that the following conditions are
  8. // met:
  9. //
  10. // * Redistributions of source code must retain the above copyright
  11. // notice, this list of conditions and the following disclaimer.
  12. // * Redistributions in binary form must reproduce the above
  13. // copyright notice, this list of conditions and the following disclaimer
  14. // in the documentation and/or other materials provided with the
  15. // distribution.
  16. // * Neither the name of Google Inc. nor the names of its
  17. // contributors may be used to endorse or promote products derived from
  18. // this software without specific prior written permission.
  19. //
  20. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. // +build go1.7
  32. package proto_test
  33. import (
  34. "fmt"
  35. "testing"
  36. "github.com/golang/protobuf/proto"
  37. tpb "github.com/golang/protobuf/proto/proto3_proto"
  38. )
  39. var (
  40. bytesBlackhole []byte
  41. msgBlackhole = new(tpb.Message)
  42. )
  43. // BenchmarkVarint32ArraySmall shows the performance on an array of small int32 fields (1 and
  44. // 2 bytes long).
  45. func BenchmarkVarint32ArraySmall(b *testing.B) {
  46. for i := uint(1); i <= 10; i++ {
  47. dist := genInt32Dist([7]int{0, 3, 1}, 1<<i)
  48. raw, err := proto.Marshal(&tpb.Message{
  49. ShortKey: dist,
  50. })
  51. if err != nil {
  52. b.Error("wrong encode", err)
  53. }
  54. b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
  55. scratchBuf := proto.NewBuffer(nil)
  56. b.ResetTimer()
  57. for k := 0; k < b.N; k++ {
  58. scratchBuf.SetBuf(raw)
  59. msgBlackhole.Reset()
  60. if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
  61. b.Error("wrong decode", err)
  62. }
  63. }
  64. })
  65. }
  66. }
  67. // BenchmarkVarint32ArrayLarge shows the performance on an array of large int32 fields (3 and
  68. // 4 bytes long, with a small number of 1, 2, 5 and 10 byte long versions).
  69. func BenchmarkVarint32ArrayLarge(b *testing.B) {
  70. for i := uint(1); i <= 10; i++ {
  71. dist := genInt32Dist([7]int{0, 1, 2, 4, 8, 1, 1}, 1<<i)
  72. raw, err := proto.Marshal(&tpb.Message{
  73. ShortKey: dist,
  74. })
  75. if err != nil {
  76. b.Error("wrong encode", err)
  77. }
  78. b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
  79. scratchBuf := proto.NewBuffer(nil)
  80. b.ResetTimer()
  81. for k := 0; k < b.N; k++ {
  82. scratchBuf.SetBuf(raw)
  83. msgBlackhole.Reset()
  84. if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
  85. b.Error("wrong decode", err)
  86. }
  87. }
  88. })
  89. }
  90. }
  91. // BenchmarkVarint64ArraySmall shows the performance on an array of small int64 fields (1 and
  92. // 2 bytes long).
  93. func BenchmarkVarint64ArraySmall(b *testing.B) {
  94. for i := uint(1); i <= 10; i++ {
  95. dist := genUint64Dist([11]int{0, 3, 1}, 1<<i)
  96. raw, err := proto.Marshal(&tpb.Message{
  97. Key: dist,
  98. })
  99. if err != nil {
  100. b.Error("wrong encode", err)
  101. }
  102. b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
  103. scratchBuf := proto.NewBuffer(nil)
  104. b.ResetTimer()
  105. for k := 0; k < b.N; k++ {
  106. scratchBuf.SetBuf(raw)
  107. msgBlackhole.Reset()
  108. if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
  109. b.Error("wrong decode", err)
  110. }
  111. }
  112. })
  113. }
  114. }
  115. // BenchmarkVarint64ArrayLarge shows the performance on an array of large int64 fields (6, 7,
  116. // and 8 bytes long with a small number of the other sizes).
  117. func BenchmarkVarint64ArrayLarge(b *testing.B) {
  118. for i := uint(1); i <= 10; i++ {
  119. dist := genUint64Dist([11]int{0, 1, 1, 2, 4, 8, 16, 32, 16, 1, 1}, 1<<i)
  120. raw, err := proto.Marshal(&tpb.Message{
  121. Key: dist,
  122. })
  123. if err != nil {
  124. b.Error("wrong encode", err)
  125. }
  126. b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
  127. scratchBuf := proto.NewBuffer(nil)
  128. b.ResetTimer()
  129. for k := 0; k < b.N; k++ {
  130. scratchBuf.SetBuf(raw)
  131. msgBlackhole.Reset()
  132. if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
  133. b.Error("wrong decode", err)
  134. }
  135. }
  136. })
  137. }
  138. }
  139. // BenchmarkVarint64ArrayMixed shows the performance of lots of small messages, each
  140. // containing a small number of large (3, 4, and 5 byte) repeated int64s.
  141. func BenchmarkVarint64ArrayMixed(b *testing.B) {
  142. for i := uint(1); i <= 1<<5; i <<= 1 {
  143. dist := genUint64Dist([11]int{0, 0, 0, 4, 6, 4, 0, 0, 0, 0, 0}, int(i))
  144. // number of sub fields
  145. for k := uint(1); k <= 1<<10; k <<= 2 {
  146. msg := &tpb.Message{}
  147. for m := uint(0); m < k; m++ {
  148. msg.Children = append(msg.Children, &tpb.Message{
  149. Key: dist,
  150. })
  151. }
  152. raw, err := proto.Marshal(msg)
  153. if err != nil {
  154. b.Error("wrong encode", err)
  155. }
  156. b.Run(fmt.Sprintf("Fields%vLen%v", k, i), func(b *testing.B) {
  157. scratchBuf := proto.NewBuffer(nil)
  158. b.ResetTimer()
  159. for k := 0; k < b.N; k++ {
  160. scratchBuf.SetBuf(raw)
  161. msgBlackhole.Reset()
  162. if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
  163. b.Error("wrong decode", err)
  164. }
  165. }
  166. })
  167. }
  168. }
  169. }
  170. // genInt32Dist generates a slice of ints that will match the size distribution of dist.
  171. // A size of 6 corresponds to a max length varint32, which is 10 bytes. The distribution
  172. // is 1-indexed. (i.e. the value at index 1 is how many 1 byte ints to create).
  173. func genInt32Dist(dist [7]int, count int) (dest []int32) {
  174. for i := 0; i < count; i++ {
  175. for k := 0; k < len(dist); k++ {
  176. var num int32
  177. switch k {
  178. case 1:
  179. num = 1<<7 - 1
  180. case 2:
  181. num = 1<<14 - 1
  182. case 3:
  183. num = 1<<21 - 1
  184. case 4:
  185. num = 1<<28 - 1
  186. case 5:
  187. num = 1<<29 - 1
  188. case 6:
  189. num = -1
  190. }
  191. for m := 0; m < dist[k]; m++ {
  192. dest = append(dest, num)
  193. }
  194. }
  195. }
  196. return
  197. }
  198. // genUint64Dist generates a slice of ints that will match the size distribution of dist.
  199. // The distribution is 1-indexed. (i.e. the value at index 1 is how many 1 byte ints to create).
  200. func genUint64Dist(dist [11]int, count int) (dest []uint64) {
  201. for i := 0; i < count; i++ {
  202. for k := 0; k < len(dist); k++ {
  203. var num uint64
  204. switch k {
  205. case 1:
  206. num = 1<<7 - 1
  207. case 2:
  208. num = 1<<14 - 1
  209. case 3:
  210. num = 1<<21 - 1
  211. case 4:
  212. num = 1<<28 - 1
  213. case 5:
  214. num = 1<<35 - 1
  215. case 6:
  216. num = 1<<42 - 1
  217. case 7:
  218. num = 1<<49 - 1
  219. case 8:
  220. num = 1<<56 - 1
  221. case 9:
  222. num = 1<<63 - 1
  223. case 10:
  224. num = 1<<64 - 1
  225. }
  226. for m := 0; m < dist[k]; m++ {
  227. dest = append(dest, num)
  228. }
  229. }
  230. }
  231. return
  232. }
  233. // BenchmarkDecodeEmpty measures the overhead of doing the minimal possible decode.
  234. func BenchmarkDecodeEmpty(b *testing.B) {
  235. raw, err := proto.Marshal(&tpb.Message{})
  236. if err != nil {
  237. b.Error("wrong encode", err)
  238. }
  239. b.ResetTimer()
  240. for i := 0; i < b.N; i++ {
  241. if err := proto.Unmarshal(raw, msgBlackhole); err != nil {
  242. b.Error("wrong decode", err)
  243. }
  244. }
  245. }