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