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