decode_test.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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 msgBlackhole = new(tpb.Message)
  40. // BenchmarkVarint32ArraySmall shows the performance on an array of small int32 fields (1 and
  41. // 2 bytes long).
  42. func BenchmarkVarint32ArraySmall(b *testing.B) {
  43. for i := uint(1); i <= 10; i++ {
  44. dist := genInt32Dist([7]int{0, 3, 1}, 1<<i)
  45. raw, err := proto.Marshal(&tpb.Message{
  46. ShortKey: dist,
  47. })
  48. if err != nil {
  49. b.Error("wrong encode", err)
  50. }
  51. b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
  52. scratchBuf := proto.NewBuffer(nil)
  53. b.ResetTimer()
  54. for k := 0; k < b.N; k++ {
  55. scratchBuf.SetBuf(raw)
  56. msgBlackhole.Reset()
  57. if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
  58. b.Error("wrong decode", err)
  59. }
  60. }
  61. })
  62. }
  63. }
  64. // BenchmarkVarint32ArrayLarge shows the performance on an array of large int32 fields (3 and
  65. // 4 bytes long, with a small number of 1, 2, 5 and 10 byte long versions).
  66. func BenchmarkVarint32ArrayLarge(b *testing.B) {
  67. for i := uint(1); i <= 10; i++ {
  68. dist := genInt32Dist([7]int{0, 1, 2, 4, 8, 1, 1}, 1<<i)
  69. raw, err := proto.Marshal(&tpb.Message{
  70. ShortKey: dist,
  71. })
  72. if err != nil {
  73. b.Error("wrong encode", err)
  74. }
  75. b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
  76. scratchBuf := proto.NewBuffer(nil)
  77. b.ResetTimer()
  78. for k := 0; k < b.N; k++ {
  79. scratchBuf.SetBuf(raw)
  80. msgBlackhole.Reset()
  81. if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
  82. b.Error("wrong decode", err)
  83. }
  84. }
  85. })
  86. }
  87. }
  88. // BenchmarkVarint64ArraySmall shows the performance on an array of small int64 fields (1 and
  89. // 2 bytes long).
  90. func BenchmarkVarint64ArraySmall(b *testing.B) {
  91. for i := uint(1); i <= 10; i++ {
  92. dist := genUint64Dist([11]int{0, 3, 1}, 1<<i)
  93. raw, err := proto.Marshal(&tpb.Message{
  94. Key: dist,
  95. })
  96. if err != nil {
  97. b.Error("wrong encode", err)
  98. }
  99. b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
  100. scratchBuf := proto.NewBuffer(nil)
  101. b.ResetTimer()
  102. for k := 0; k < b.N; k++ {
  103. scratchBuf.SetBuf(raw)
  104. msgBlackhole.Reset()
  105. if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
  106. b.Error("wrong decode", err)
  107. }
  108. }
  109. })
  110. }
  111. }
  112. // BenchmarkVarint64ArrayLarge shows the performance on an array of large int64 fields (6, 7,
  113. // and 8 bytes long with a small number of the other sizes).
  114. func BenchmarkVarint64ArrayLarge(b *testing.B) {
  115. for i := uint(1); i <= 10; i++ {
  116. dist := genUint64Dist([11]int{0, 1, 1, 2, 4, 8, 16, 32, 16, 1, 1}, 1<<i)
  117. raw, err := proto.Marshal(&tpb.Message{
  118. Key: dist,
  119. })
  120. if err != nil {
  121. b.Error("wrong encode", err)
  122. }
  123. b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
  124. scratchBuf := proto.NewBuffer(nil)
  125. b.ResetTimer()
  126. for k := 0; k < b.N; k++ {
  127. scratchBuf.SetBuf(raw)
  128. msgBlackhole.Reset()
  129. if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
  130. b.Error("wrong decode", err)
  131. }
  132. }
  133. })
  134. }
  135. }
  136. // BenchmarkVarint64ArrayMixed shows the performance of lots of small messages, each
  137. // containing a small number of large (3, 4, and 5 byte) repeated int64s.
  138. func BenchmarkVarint64ArrayMixed(b *testing.B) {
  139. for i := uint(1); i <= 1<<5; i <<= 1 {
  140. dist := genUint64Dist([11]int{0, 0, 0, 4, 6, 4, 0, 0, 0, 0, 0}, int(i))
  141. // number of sub fields
  142. for k := uint(1); k <= 1<<10; k <<= 2 {
  143. msg := &tpb.Message{}
  144. for m := uint(0); m < k; m++ {
  145. msg.Children = append(msg.Children, &tpb.Message{
  146. Key: dist,
  147. })
  148. }
  149. raw, err := proto.Marshal(msg)
  150. if err != nil {
  151. b.Error("wrong encode", err)
  152. }
  153. b.Run(fmt.Sprintf("Fields%vLen%v", k, i), func(b *testing.B) {
  154. scratchBuf := proto.NewBuffer(nil)
  155. b.ResetTimer()
  156. for k := 0; k < b.N; k++ {
  157. scratchBuf.SetBuf(raw)
  158. msgBlackhole.Reset()
  159. if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
  160. b.Error("wrong decode", err)
  161. }
  162. }
  163. })
  164. }
  165. }
  166. }
  167. // genInt32Dist generates a slice of ints that will match the size distribution of dist.
  168. // A size of 6 corresponds to a max length varint32, which is 10 bytes. The distribution
  169. // is 1-indexed. (i.e. the value at index 1 is how many 1 byte ints to create).
  170. func genInt32Dist(dist [7]int, count int) (dest []int32) {
  171. for i := 0; i < count; i++ {
  172. for k := 0; k < len(dist); k++ {
  173. var num int32
  174. switch k {
  175. case 1:
  176. num = 1<<7 - 1
  177. case 2:
  178. num = 1<<14 - 1
  179. case 3:
  180. num = 1<<21 - 1
  181. case 4:
  182. num = 1<<28 - 1
  183. case 5:
  184. num = 1<<29 - 1
  185. case 6:
  186. num = -1
  187. }
  188. for m := 0; m < dist[k]; m++ {
  189. dest = append(dest, num)
  190. }
  191. }
  192. }
  193. return
  194. }
  195. // genUint64Dist generates a slice of ints that will match the size distribution of dist.
  196. // The distribution is 1-indexed. (i.e. the value at index 1 is how many 1 byte ints to create).
  197. func genUint64Dist(dist [11]int, count int) (dest []uint64) {
  198. for i := 0; i < count; i++ {
  199. for k := 0; k < len(dist); k++ {
  200. var num uint64
  201. switch k {
  202. case 1:
  203. num = 1<<7 - 1
  204. case 2:
  205. num = 1<<14 - 1
  206. case 3:
  207. num = 1<<21 - 1
  208. case 4:
  209. num = 1<<28 - 1
  210. case 5:
  211. num = 1<<35 - 1
  212. case 6:
  213. num = 1<<42 - 1
  214. case 7:
  215. num = 1<<49 - 1
  216. case 8:
  217. num = 1<<56 - 1
  218. case 9:
  219. num = 1<<63 - 1
  220. case 10:
  221. num = 1<<64 - 1
  222. }
  223. for m := 0; m < dist[k]; m++ {
  224. dest = append(dest, num)
  225. }
  226. }
  227. }
  228. return
  229. }
  230. // BenchmarkDecodeEmpty measures the overhead of doing the minimal possible decode.
  231. func BenchmarkDecodeEmpty(b *testing.B) {
  232. raw, err := proto.Marshal(&tpb.Message{})
  233. if err != nil {
  234. b.Error("wrong encode", err)
  235. }
  236. b.ResetTimer()
  237. for i := 0; i < b.N; i++ {
  238. if err := proto.Unmarshal(raw, msgBlackhole); err != nil {
  239. b.Error("wrong decode", err)
  240. }
  241. }
  242. }