1#region Copyright notice and license
2// Protocol Buffers - Google's data interchange format
3// Copyright 2019 Google Inc.  All rights reserved.
4// https://github.com/protocolbuffers/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#endregion
32
33using BenchmarkDotNet.Attributes;
34using System;
35using System.Collections.Generic;
36using System.IO;
37using System.Linq;
38using System.Buffers;
39using Google.Protobuf.WellKnownTypes;
40using Benchmarks.Proto3;
41
42namespace Google.Protobuf.Benchmarks
43{
44    /// <summary>
45    /// Benchmark that tests parsing performance for various messages.
46    /// </summary>
47    [MemoryDiagnoser]
48    public class ParseMessagesBenchmark
49    {
50        const int MaxMessages = 100;
51
52        SubTest manyWrapperFieldsTest = new SubTest(CreateManyWrapperFieldsMessage(), ManyWrapperFieldsMessage.Parser, () => new ManyWrapperFieldsMessage(), MaxMessages);
53        SubTest manyPrimitiveFieldsTest = new SubTest(CreateManyPrimitiveFieldsMessage(), ManyPrimitiveFieldsMessage.Parser, () => new ManyPrimitiveFieldsMessage(), MaxMessages);
54        SubTest repeatedFieldTest = new SubTest(CreateRepeatedFieldMessage(), GoogleMessage1.Parser, () => new GoogleMessage1(), MaxMessages);
55        SubTest emptyMessageTest = new SubTest(new Empty(), Empty.Parser, () => new Empty(), MaxMessages);
56
57        public IEnumerable<int> MessageCountValues => new[] { 10, 100 };
58
59        [GlobalSetup]
60        public void GlobalSetup()
61        {
62        }
63
64        [Benchmark]
65        public IMessage ManyWrapperFieldsMessage_ParseFromByteArray()
66        {
67            return manyWrapperFieldsTest.ParseFromByteArray();
68        }
69
70        [Benchmark]
71        public IMessage ManyWrapperFieldsMessage_ParseFromReadOnlySequence()
72        {
73            return manyWrapperFieldsTest.ParseFromReadOnlySequence();
74        }
75
76        [Benchmark]
77        public IMessage ManyPrimitiveFieldsMessage_ParseFromByteArray()
78        {
79            return manyPrimitiveFieldsTest.ParseFromByteArray();
80        }
81
82        [Benchmark]
83        public IMessage ManyPrimitiveFieldsMessage_ParseFromReadOnlySequence()
84        {
85            return manyPrimitiveFieldsTest.ParseFromReadOnlySequence();
86        }
87
88        [Benchmark]
89        public IMessage RepeatedFieldMessage_ParseFromByteArray()
90        {
91            return repeatedFieldTest.ParseFromByteArray();
92        }
93
94        [Benchmark]
95        public IMessage RepeatedFieldMessage_ParseFromReadOnlySequence()
96        {
97            return repeatedFieldTest.ParseFromReadOnlySequence();
98        }
99
100        [Benchmark]
101        public IMessage EmptyMessage_ParseFromByteArray()
102        {
103            return emptyMessageTest.ParseFromByteArray();
104        }
105
106        [Benchmark]
107        public IMessage EmptyMessage_ParseFromReadOnlySequence()
108        {
109            return emptyMessageTest.ParseFromReadOnlySequence();
110        }
111
112        [Benchmark]
113        [ArgumentsSource(nameof(MessageCountValues))]
114        public void ManyWrapperFieldsMessage_ParseDelimitedMessagesFromByteArray(int messageCount)
115        {
116            manyWrapperFieldsTest.ParseDelimitedMessagesFromByteArray(messageCount);
117        }
118
119        [Benchmark]
120        [ArgumentsSource(nameof(MessageCountValues))]
121        public void ManyWrapperFieldsMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount)
122        {
123            manyWrapperFieldsTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount);
124        }
125
126        [Benchmark]
127        [ArgumentsSource(nameof(MessageCountValues))]
128        public void ManyPrimitiveFieldsMessage_ParseDelimitedMessagesFromByteArray(int messageCount)
129        {
130            manyPrimitiveFieldsTest.ParseDelimitedMessagesFromByteArray(messageCount);
131        }
132
133        [Benchmark]
134        [ArgumentsSource(nameof(MessageCountValues))]
135        public void ManyPrimitiveFieldsMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount)
136        {
137            manyPrimitiveFieldsTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount);
138        }
139
140        [Benchmark]
141        [ArgumentsSource(nameof(MessageCountValues))]
142        public void RepeatedFieldMessage_ParseDelimitedMessagesFromByteArray(int messageCount)
143        {
144            repeatedFieldTest.ParseDelimitedMessagesFromByteArray(messageCount);
145        }
146
147        [Benchmark]
148        [ArgumentsSource(nameof(MessageCountValues))]
149        public void RepeatedFieldMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount)
150        {
151            repeatedFieldTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount);
152        }
153
154        public static ManyWrapperFieldsMessage CreateManyWrapperFieldsMessage()
155        {
156            // Example data match data of an internal benchmarks
157            return new ManyWrapperFieldsMessage()
158            {
159                Int64Field19 = 123,
160                Int64Field37 = 1000032,
161                Int64Field26 = 3453524500,
162                DoubleField79 = 1.2,
163                DoubleField25 = 234,
164                DoubleField9 = 123.3,
165                DoubleField28 = 23,
166                DoubleField7 = 234,
167                DoubleField50 = 2.45
168            };
169        }
170
171        public static ManyPrimitiveFieldsMessage CreateManyPrimitiveFieldsMessage()
172        {
173            // Example data match data of an internal benchmarks
174            return new ManyPrimitiveFieldsMessage()
175            {
176                Int64Field19 = 123,
177                Int64Field37 = 1000032,
178                Int64Field26 = 3453524500,
179                DoubleField79 = 1.2,
180                DoubleField25 = 234,
181                DoubleField9 = 123.3,
182                DoubleField28 = 23,
183                DoubleField7 = 234,
184                DoubleField50 = 2.45
185            };
186        }
187
188        public static GoogleMessage1 CreateRepeatedFieldMessage()
189        {
190            // Message with a repeated fixed length item collection
191            var message = new GoogleMessage1();
192            for (ulong i = 0; i < 1000; i++)
193            {
194                message.Field5.Add(i);
195            }
196            return message;
197        }
198
199        private class SubTest
200        {
201            private readonly IMessage message;
202            private readonly MessageParser parser;
203            private readonly Func<IMessage> factory;
204            private readonly byte[] data;
205            private readonly byte[] multipleMessagesData;
206
207            private ReadOnlySequence<byte> dataSequence;
208            private ReadOnlySequence<byte> multipleMessagesDataSequence;
209
210            public SubTest(IMessage message, MessageParser parser, Func<IMessage> factory, int maxMessageCount)
211            {
212                this.message = message;
213                this.parser = parser;
214                this.factory = factory;
215                this.data = message.ToByteArray();
216                this.multipleMessagesData = CreateBufferWithMultipleMessages(message, maxMessageCount);
217                this.dataSequence = new ReadOnlySequence<byte>(this.data);
218                this.multipleMessagesDataSequence = new ReadOnlySequence<byte>(this.multipleMessagesData);
219            }
220
221            public IMessage ParseFromByteArray() => parser.ParseFrom(data);
222
223            public IMessage ParseFromReadOnlySequence() => parser.ParseFrom(dataSequence);
224
225            public void ParseDelimitedMessagesFromByteArray(int messageCount)
226            {
227                var input = new CodedInputStream(multipleMessagesData);
228                for (int i = 0; i < messageCount; i++)
229                {
230                    var msg = factory();
231                    input.ReadMessage(msg);
232                }
233            }
234
235            public void ParseDelimitedMessagesFromReadOnlySequence(int messageCount)
236            {
237                ParseContext.Initialize(multipleMessagesDataSequence, out ParseContext ctx);
238                for (int i = 0; i < messageCount; i++)
239                {
240                    var msg = factory();
241                    ctx.ReadMessage(msg);
242                }
243            }
244
245            private static byte[] CreateBufferWithMultipleMessages(IMessage msg, int msgCount)
246            {
247                var ms = new MemoryStream();
248                var cos = new CodedOutputStream(ms);
249                for (int i = 0; i < msgCount; i++)
250                {
251                    cos.WriteMessage(msg);
252                }
253                cos.Flush();
254                return ms.ToArray();
255            }
256        }
257    }
258}
259