internal/benchmarks/micro: add a place for microbenchmarks

Add a place to put microbenchmarks used to justify performance-related changes.

Change-Id: I6e90a3500594b3f6297cee0b8e321a50d0a556ca
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/216480
Reviewed-by: Joe Tsai <joetsai@google.com>
diff --git a/internal/benchmarks/micro/micro_test.go b/internal/benchmarks/micro/micro_test.go
new file mode 100644
index 0000000..20a6fea
--- /dev/null
+++ b/internal/benchmarks/micro/micro_test.go
@@ -0,0 +1,134 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This package contains microbenchmarks exercising specific areas of interest.
+// The benchmarks here are not comprehensive and are not necessarily indicative
+// real-world performance.
+
+package micro_test
+
+import (
+	"testing"
+
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/types/known/emptypb"
+
+	micropb "google.golang.org/protobuf/internal/testprotos/benchmarks/micro"
+	testpb "google.golang.org/protobuf/internal/testprotos/test"
+)
+
+// BenchmarkEmptyMessage tests a google.protobuf.Empty.
+//
+// It measures per-operation overhead.
+func BenchmarkEmptyMessage(b *testing.B) {
+	b.Run("Wire/Marshal", func(b *testing.B) {
+		b.RunParallel(func(pb *testing.PB) {
+			m := &emptypb.Empty{}
+			for pb.Next() {
+				if _, err := proto.Marshal(m); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+	})
+	b.Run("Wire/Unmarshal", func(b *testing.B) {
+		opts := proto.UnmarshalOptions{
+			Merge: true,
+		}
+		b.RunParallel(func(pb *testing.PB) {
+			m := &emptypb.Empty{}
+			for pb.Next() {
+				if err := opts.Unmarshal([]byte{}, m); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+	})
+}
+
+// BenchmarkRepeatedInt32 tests a message containing 500 non-packed repeated int32s.
+//
+// For unmarshal operations, it measures the cost of the field decode loop, since each
+// item in the repeated field has an individual tag and value.
+func BenchmarkRepeatedInt32(b *testing.B) {
+	m := &testpb.TestAllTypes{}
+	for i := int32(0); i < 500; i++ {
+		m.RepeatedInt32 = append(m.RepeatedInt32, i)
+	}
+	w, err := proto.Marshal(m)
+	if err != nil {
+		b.Fatal(err)
+	}
+	b.Run("Wire/Marshal", func(b *testing.B) {
+		b.RunParallel(func(pb *testing.PB) {
+			for pb.Next() {
+				if _, err := proto.Marshal(m); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+	})
+	b.Run("Wire/Unmarshal", func(b *testing.B) {
+		opts := proto.UnmarshalOptions{
+			Merge: true,
+		}
+		b.RunParallel(func(pb *testing.PB) {
+			m := &testpb.TestAllTypes{}
+			for pb.Next() {
+				m.RepeatedInt32 = m.RepeatedInt32[:0]
+				if err := opts.Unmarshal(w, m); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+	})
+}
+
+// BenchmarkRequired tests a message containing a required field.
+func BenchmarkRequired(b *testing.B) {
+	m := &micropb.SixteenRequired{
+		F1:  proto.Int32(1),
+		F2:  proto.Int32(1),
+		F3:  proto.Int32(1),
+		F4:  proto.Int32(1),
+		F5:  proto.Int32(1),
+		F6:  proto.Int32(1),
+		F7:  proto.Int32(1),
+		F8:  proto.Int32(1),
+		F9:  proto.Int32(1),
+		F10: proto.Int32(1),
+		F11: proto.Int32(1),
+		F12: proto.Int32(1),
+		F13: proto.Int32(1),
+		F14: proto.Int32(1),
+		F15: proto.Int32(1),
+		F16: proto.Int32(1),
+	}
+	w, err := proto.Marshal(m)
+	if err != nil {
+		b.Fatal(err)
+	}
+	b.Run("Wire/Marshal", func(b *testing.B) {
+		b.RunParallel(func(pb *testing.PB) {
+			for pb.Next() {
+				if _, err := proto.Marshal(m); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+	})
+	b.Run("Wire/Unmarshal", func(b *testing.B) {
+		opts := proto.UnmarshalOptions{
+			Merge: true,
+		}
+		b.RunParallel(func(pb *testing.PB) {
+			m := &micropb.SixteenRequired{}
+			for pb.Next() {
+				if err := opts.Unmarshal(w, m); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+	})
+}
diff --git a/internal/testprotos/benchmarks/micro/micro.pb.go b/internal/testprotos/benchmarks/micro/micro.pb.go
new file mode 100644
index 0000000..eb8bc2e
--- /dev/null
+++ b/internal/testprotos/benchmarks/micro/micro.pb.go
@@ -0,0 +1,277 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: benchmarks/micro/micro.proto
+
+package micro
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+type SixteenRequired struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	F1  *int32 `protobuf:"varint,1,req,name=f1" json:"f1,omitempty"`
+	F2  *int32 `protobuf:"varint,2,req,name=f2" json:"f2,omitempty"`
+	F3  *int32 `protobuf:"varint,3,req,name=f3" json:"f3,omitempty"`
+	F4  *int32 `protobuf:"varint,4,req,name=f4" json:"f4,omitempty"`
+	F5  *int32 `protobuf:"varint,5,req,name=f5" json:"f5,omitempty"`
+	F6  *int32 `protobuf:"varint,6,req,name=f6" json:"f6,omitempty"`
+	F7  *int32 `protobuf:"varint,7,req,name=f7" json:"f7,omitempty"`
+	F8  *int32 `protobuf:"varint,8,req,name=f8" json:"f8,omitempty"`
+	F9  *int32 `protobuf:"varint,9,req,name=f9" json:"f9,omitempty"`
+	F10 *int32 `protobuf:"varint,10,req,name=f10" json:"f10,omitempty"`
+	F11 *int32 `protobuf:"varint,11,req,name=f11" json:"f11,omitempty"`
+	F12 *int32 `protobuf:"varint,12,req,name=f12" json:"f12,omitempty"`
+	F13 *int32 `protobuf:"varint,13,req,name=f13" json:"f13,omitempty"`
+	F14 *int32 `protobuf:"varint,14,req,name=f14" json:"f14,omitempty"`
+	F15 *int32 `protobuf:"varint,15,req,name=f15" json:"f15,omitempty"`
+	F16 *int32 `protobuf:"varint,16,req,name=f16" json:"f16,omitempty"`
+}
+
+func (x *SixteenRequired) Reset() {
+	*x = SixteenRequired{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_benchmarks_micro_micro_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SixteenRequired) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SixteenRequired) ProtoMessage() {}
+
+func (x *SixteenRequired) ProtoReflect() protoreflect.Message {
+	mi := &file_benchmarks_micro_micro_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SixteenRequired.ProtoReflect.Descriptor instead.
+func (*SixteenRequired) Descriptor() ([]byte, []int) {
+	return file_benchmarks_micro_micro_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *SixteenRequired) GetF1() int32 {
+	if x != nil && x.F1 != nil {
+		return *x.F1
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF2() int32 {
+	if x != nil && x.F2 != nil {
+		return *x.F2
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF3() int32 {
+	if x != nil && x.F3 != nil {
+		return *x.F3
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF4() int32 {
+	if x != nil && x.F4 != nil {
+		return *x.F4
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF5() int32 {
+	if x != nil && x.F5 != nil {
+		return *x.F5
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF6() int32 {
+	if x != nil && x.F6 != nil {
+		return *x.F6
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF7() int32 {
+	if x != nil && x.F7 != nil {
+		return *x.F7
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF8() int32 {
+	if x != nil && x.F8 != nil {
+		return *x.F8
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF9() int32 {
+	if x != nil && x.F9 != nil {
+		return *x.F9
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF10() int32 {
+	if x != nil && x.F10 != nil {
+		return *x.F10
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF11() int32 {
+	if x != nil && x.F11 != nil {
+		return *x.F11
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF12() int32 {
+	if x != nil && x.F12 != nil {
+		return *x.F12
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF13() int32 {
+	if x != nil && x.F13 != nil {
+		return *x.F13
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF14() int32 {
+	if x != nil && x.F14 != nil {
+		return *x.F14
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF15() int32 {
+	if x != nil && x.F15 != nil {
+		return *x.F15
+	}
+	return 0
+}
+
+func (x *SixteenRequired) GetF16() int32 {
+	if x != nil && x.F16 != nil {
+		return *x.F16
+	}
+	return 0
+}
+
+var File_benchmarks_micro_micro_proto protoreflect.FileDescriptor
+
+var file_benchmarks_micro_micro_proto_rawDesc = []byte{
+	0x0a, 0x1c, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x2f, 0x6d, 0x69, 0x63,
+	0x72, 0x6f, 0x2f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f,
+	0x67, 0x6f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x62, 0x65,
+	0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x74, 0x22,
+	0x9f, 0x02, 0x0a, 0x0f, 0x53, 0x69, 0x78, 0x74, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x69,
+	0x72, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x31, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05, 0x52,
+	0x02, 0x66, 0x31, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x32, 0x18, 0x02, 0x20, 0x02, 0x28, 0x05, 0x52,
+	0x02, 0x66, 0x32, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x33, 0x18, 0x03, 0x20, 0x02, 0x28, 0x05, 0x52,
+	0x02, 0x66, 0x33, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x34, 0x18, 0x04, 0x20, 0x02, 0x28, 0x05, 0x52,
+	0x02, 0x66, 0x34, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x35, 0x18, 0x05, 0x20, 0x02, 0x28, 0x05, 0x52,
+	0x02, 0x66, 0x35, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x36, 0x18, 0x06, 0x20, 0x02, 0x28, 0x05, 0x52,
+	0x02, 0x66, 0x36, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x37, 0x18, 0x07, 0x20, 0x02, 0x28, 0x05, 0x52,
+	0x02, 0x66, 0x37, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x38, 0x18, 0x08, 0x20, 0x02, 0x28, 0x05, 0x52,
+	0x02, 0x66, 0x38, 0x12, 0x0e, 0x0a, 0x02, 0x66, 0x39, 0x18, 0x09, 0x20, 0x02, 0x28, 0x05, 0x52,
+	0x02, 0x66, 0x39, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x31, 0x30, 0x18, 0x0a, 0x20, 0x02, 0x28, 0x05,
+	0x52, 0x03, 0x66, 0x31, 0x30, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x31, 0x31, 0x18, 0x0b, 0x20, 0x02,
+	0x28, 0x05, 0x52, 0x03, 0x66, 0x31, 0x31, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x31, 0x32, 0x18, 0x0c,
+	0x20, 0x02, 0x28, 0x05, 0x52, 0x03, 0x66, 0x31, 0x32, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x31, 0x33,
+	0x18, 0x0d, 0x20, 0x02, 0x28, 0x05, 0x52, 0x03, 0x66, 0x31, 0x33, 0x12, 0x10, 0x0a, 0x03, 0x66,
+	0x31, 0x34, 0x18, 0x0e, 0x20, 0x02, 0x28, 0x05, 0x52, 0x03, 0x66, 0x31, 0x34, 0x12, 0x10, 0x0a,
+	0x03, 0x66, 0x31, 0x35, 0x18, 0x0f, 0x20, 0x02, 0x28, 0x05, 0x52, 0x03, 0x66, 0x31, 0x35, 0x12,
+	0x10, 0x0a, 0x03, 0x66, 0x31, 0x36, 0x18, 0x10, 0x20, 0x02, 0x28, 0x05, 0x52, 0x03, 0x66, 0x31,
+	0x36, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61,
+	0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f,
+	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x73, 0x2f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x2f, 0x6d,
+	0x69, 0x63, 0x72, 0x6f,
+}
+
+var (
+	file_benchmarks_micro_micro_proto_rawDescOnce sync.Once
+	file_benchmarks_micro_micro_proto_rawDescData = file_benchmarks_micro_micro_proto_rawDesc
+)
+
+func file_benchmarks_micro_micro_proto_rawDescGZIP() []byte {
+	file_benchmarks_micro_micro_proto_rawDescOnce.Do(func() {
+		file_benchmarks_micro_micro_proto_rawDescData = protoimpl.X.CompressGZIP(file_benchmarks_micro_micro_proto_rawDescData)
+	})
+	return file_benchmarks_micro_micro_proto_rawDescData
+}
+
+var file_benchmarks_micro_micro_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_benchmarks_micro_micro_proto_goTypes = []interface{}{
+	(*SixteenRequired)(nil), // 0: goproto.proto.benchmarks.microt.SixteenRequired
+}
+var file_benchmarks_micro_micro_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_benchmarks_micro_micro_proto_init() }
+func file_benchmarks_micro_micro_proto_init() {
+	if File_benchmarks_micro_micro_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_benchmarks_micro_micro_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SixteenRequired); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_benchmarks_micro_micro_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_benchmarks_micro_micro_proto_goTypes,
+		DependencyIndexes: file_benchmarks_micro_micro_proto_depIdxs,
+		MessageInfos:      file_benchmarks_micro_micro_proto_msgTypes,
+	}.Build()
+	File_benchmarks_micro_micro_proto = out.File
+	file_benchmarks_micro_micro_proto_rawDesc = nil
+	file_benchmarks_micro_micro_proto_goTypes = nil
+	file_benchmarks_micro_micro_proto_depIdxs = nil
+}
diff --git a/internal/testprotos/benchmarks/micro/micro.proto b/internal/testprotos/benchmarks/micro/micro.proto
new file mode 100644
index 0000000..a0303c1
--- /dev/null
+++ b/internal/testprotos/benchmarks/micro/micro.proto
@@ -0,0 +1,28 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+syntax = "proto2";
+
+package goproto.proto.benchmarks.microt;
+
+option go_package = "google.golang.org/protobuf/internal/testprotos/benchmarks/micro";
+
+message SixteenRequired {
+  required int32 f1 = 1;
+  required int32 f2 = 2;
+  required int32 f3 = 3;
+  required int32 f4 = 4;
+  required int32 f5 = 5;
+  required int32 f6 = 6;
+  required int32 f7 = 7;
+  required int32 f8 = 8;
+  required int32 f9 = 9;
+  required int32 f10 = 10;
+  required int32 f11 = 11;
+  required int32 f12 = 12;
+  required int32 f13 = 13;
+  required int32 f14 = 14;
+  required int32 f15 = 15;
+  required int32 f16 = 16;
+}