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