First pass at benchmarking for C#

pull/5890/head
Jon Skeet 6 years ago committed by Jon Skeet
parent db1d4dc4c1
commit 233f4865ee
  1. 17
      benchmarks/README.md
  2. 3
      csharp/.gitignore
  3. 6
      csharp/generate_protos.sh
  4. 1980
      csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs
  5. 250
      csharp/src/Google.Protobuf.Benchmarks/Benchmarks.cs
  6. 17
      csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj
  7. 46
      csharp/src/Google.Protobuf.Benchmarks/Program.cs
  8. 120
      csharp/src/Google.Protobuf.Benchmarks/SerializationBenchmark.cs
  9. 89
      csharp/src/Google.Protobuf.Benchmarks/SerializationConfig.cs
  10. 19
      csharp/src/Google.Protobuf.sln

@ -65,6 +65,12 @@ include PHP protobuf's src and build the c extension if required.
### Node.js
Node.js benchmark need [node](https://nodejs.org/en/)(higher than V6) and [npm](https://www.npmjs.com/) package manager installed. This benchmark is using the [benchmark](https://www.npmjs.com/package/benchmark) framework to test, which needn't to manually install. And another prerequisite is [protobuf js](https://github.com/protocolbuffers/protobuf/tree/master/js), which needn't to manually install either
### C#
The C# benchmark code is built as part of the main Google.Protobuf
solution. It requires the .NET Core SDK, and depends on
[BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet), which
will be downloaded automatically.
### Big data
There's some optional big testing data which is not included in the directory
@ -74,7 +80,7 @@ initially, you need to run the following command to download the testing data:
$ ./download_data.sh
```
After doing this the big data file will automaticly generated in the
After doing this the big data file will automatically generated in the
benchmark directory.
## Run instructions
@ -209,6 +215,15 @@ $ make js-benchmark
$ ./js-benchmark $(specific generated dataset file name)
```
### C#
From `csharp/src/Google.Protobuf.Benchmarks`, run:
```
$ dotnet run -c Release
```
We intend to add support for this within the makefile in due course.
## Benchmark datasets
Each data set is in the format of benchmarks.proto:

3
csharp/.gitignore vendored

@ -29,3 +29,6 @@ mono/*.exe
mono/*.dll
lib/protoc.exe
*.ncrunch*
# Benchmark output
BenchmarkDotNet.Artifacts/

@ -61,3 +61,9 @@ $PROTOC -Iexamples -Isrc --csharp_out=csharp/src/AddressBook \
$PROTOC -Iconformance -Isrc --csharp_out=csharp/src/Google.Protobuf.Conformance \
conformance/conformance.proto
# Benchmark protos
$PROTOC -Ibenchmarks \
benchmarks/datasets/google_message1/proto3/*.proto \
benchmarks/benchmarks.proto \
--csharp_out=csharp/src/Google.Protobuf.Benchmarks

@ -0,0 +1,250 @@
// <auto-generated>
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: benchmarks.proto
// </auto-generated>
#pragma warning disable 1591, 0612, 3021
#region Designer generated code
using pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
namespace Benchmarks {
/// <summary>Holder for reflection information generated from benchmarks.proto</summary>
public static partial class BenchmarksReflection {
#region Descriptor
/// <summary>File descriptor for benchmarks.proto</summary>
public static pbr::FileDescriptor Descriptor {
get { return descriptor; }
}
private static pbr::FileDescriptor descriptor;
static BenchmarksReflection() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"ChBiZW5jaG1hcmtzLnByb3RvEgpiZW5jaG1hcmtzIkcKEEJlbmNobWFya0Rh",
"dGFzZXQSDAoEbmFtZRgBIAEoCRIUCgxtZXNzYWdlX25hbWUYAiABKAkSDwoH",
"cGF5bG9hZBgDIAMoDEIgCh5jb20uZ29vZ2xlLnByb3RvYnVmLmJlbmNobWFy",
"a3NiBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
new pbr::GeneratedClrTypeInfo(typeof(global::Benchmarks.BenchmarkDataset), global::Benchmarks.BenchmarkDataset.Parser, new[]{ "Name", "MessageName", "Payload" }, null, null, null)
}));
}
#endregion
}
#region Messages
public sealed partial class BenchmarkDataset : pb::IMessage<BenchmarkDataset> {
private static readonly pb::MessageParser<BenchmarkDataset> _parser = new pb::MessageParser<BenchmarkDataset>(() => new BenchmarkDataset());
private pb::UnknownFieldSet _unknownFields;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser<BenchmarkDataset> Parser { get { return _parser; } }
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pbr::MessageDescriptor Descriptor {
get { return global::Benchmarks.BenchmarksReflection.Descriptor.MessageTypes[0]; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public BenchmarkDataset() {
OnConstruction();
}
partial void OnConstruction();
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public BenchmarkDataset(BenchmarkDataset other) : this() {
name_ = other.name_;
messageName_ = other.messageName_;
payload_ = other.payload_.Clone();
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public BenchmarkDataset Clone() {
return new BenchmarkDataset(this);
}
/// <summary>Field number for the "name" field.</summary>
public const int NameFieldNumber = 1;
private string name_ = "";
/// <summary>
/// Name of the benchmark dataset. This should be unique across all datasets.
/// Should only contain word characters: [a-zA-Z0-9_]
/// </summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public string Name {
get { return name_; }
set {
name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
/// <summary>Field number for the "message_name" field.</summary>
public const int MessageNameFieldNumber = 2;
private string messageName_ = "";
/// <summary>
/// Fully-qualified name of the protobuf message for this dataset.
/// It will be one of the messages defined benchmark_messages_proto2.proto
/// or benchmark_messages_proto3.proto.
///
/// Implementations that do not support reflection can implement this with
/// an explicit "if/else" chain that lists every known message defined
/// in those files.
/// </summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public string MessageName {
get { return messageName_; }
set {
messageName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
}
}
/// <summary>Field number for the "payload" field.</summary>
public const int PayloadFieldNumber = 3;
private static readonly pb::FieldCodec<pb::ByteString> _repeated_payload_codec
= pb::FieldCodec.ForBytes(26);
private readonly pbc::RepeatedField<pb::ByteString> payload_ = new pbc::RepeatedField<pb::ByteString>();
/// <summary>
/// The payload(s) for this dataset. They should be parsed or serialized
/// in sequence, in a loop, ie.
///
/// while (!benchmarkDone) { // Benchmark runner decides when to exit.
/// for (i = 0; i &lt; benchmark.payload.length; i++) {
/// parse(benchmark.payload[i])
/// }
/// }
///
/// This is intended to let datasets include a variety of data to provide
/// potentially more realistic results than just parsing the same message
/// over and over. A single message parsed repeatedly could yield unusually
/// good branch prediction performance.
/// </summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public pbc::RepeatedField<pb::ByteString> Payload {
get { return payload_; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override bool Equals(object other) {
return Equals(other as BenchmarkDataset);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool Equals(BenchmarkDataset other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (Name != other.Name) return false;
if (MessageName != other.MessageName) return false;
if(!payload_.Equals(other.payload_)) return false;
return Equals(_unknownFields, other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override int GetHashCode() {
int hash = 1;
if (Name.Length != 0) hash ^= Name.GetHashCode();
if (MessageName.Length != 0) hash ^= MessageName.GetHashCode();
hash ^= payload_.GetHashCode();
if (_unknownFields != null) {
hash ^= _unknownFields.GetHashCode();
}
return hash;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override string ToString() {
return pb::JsonFormatter.ToDiagnosticString(this);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void WriteTo(pb::CodedOutputStream output) {
if (Name.Length != 0) {
output.WriteRawTag(10);
output.WriteString(Name);
}
if (MessageName.Length != 0) {
output.WriteRawTag(18);
output.WriteString(MessageName);
}
payload_.WriteTo(output, _repeated_payload_codec);
if (_unknownFields != null) {
_unknownFields.WriteTo(output);
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public int CalculateSize() {
int size = 0;
if (Name.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
}
if (MessageName.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(MessageName);
}
size += payload_.CalculateSize(_repeated_payload_codec);
if (_unknownFields != null) {
size += _unknownFields.CalculateSize();
}
return size;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom(BenchmarkDataset other) {
if (other == null) {
return;
}
if (other.Name.Length != 0) {
Name = other.Name;
}
if (other.MessageName.Length != 0) {
MessageName = other.MessageName;
}
payload_.Add(other.payload_);
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
break;
case 10: {
Name = input.ReadString();
break;
}
case 18: {
MessageName = input.ReadString();
break;
}
case 26: {
payload_.AddEntriesFrom(input, _repeated_payload_codec);
break;
}
}
}
}
}
#endregion
}
#endregion Designer generated code

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.4" />
<ProjectReference Include="..\Google.Protobuf\Google.Protobuf.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\..\..\benchmarks\datasets\google_message1\proto3\dataset.google_message1_proto3.pb" />
</ItemGroup>
</Project>

@ -0,0 +1,46 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2019 Google Inc. All rights reserved.
// https://github.com/protocolbuffers/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using BenchmarkDotNet.Running;
namespace Google.Protobuf.Benchmarks
{
/// <summary>
/// Entry point, that currently runs the sole benchmark we have.
/// Eventually we might want to be able to specify a particular dataset
/// from the command line.
/// </summary>
class Program
{
static void Main() => BenchmarkRunner.Run<SerializationBenchmark>();
}
}

@ -0,0 +1,120 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2019 Google Inc. All rights reserved.
// https://github.com/protocolbuffers/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using BenchmarkDotNet.Attributes;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Google.Protobuf.Benchmarks
{
/// <summary>
/// Benchmark for serializing (to a MemoryStream) and deserializing (from a ByteString).
/// Over time we may wish to test the various different approaches to serialization and deserialization separately.
/// </summary>
[MemoryDiagnoser]
public class SerializationBenchmark
{
/// <summary>
/// All the configurations to be tested. Add more datasets to the array as they're available.
/// (When C# supports proto2, this will increase significantly.)
/// </summary>
public static SerializationConfig[] Configurations => new[]
{
new SerializationConfig("dataset.google_message1_proto3.pb")
};
[ParamsSource(nameof(Configurations))]
public SerializationConfig Configuration { get; set; }
private MessageParser parser;
/// <summary>
/// Each data set can contain multiple messages in a single file.
/// Each "write" operation should write each message in turn, and each "parse"
/// operation should parse each message in turn.
/// </summary>
private List<SubTest> subTests;
[GlobalSetup]
public void GlobalSetup()
{
parser = Configuration.Parser;
subTests = Configuration.Payloads.Select(p => new SubTest(p, parser.ParseFrom(p))).ToList();
}
[Benchmark]
public void WriteToStream() => subTests.ForEach(item => item.WriteToStream());
[Benchmark]
public void ToByteArray() => subTests.ForEach(item => item.ToByteArray());
[Benchmark]
public void ParseFromByteString() => subTests.ForEach(item => item.ParseFromByteString(parser));
[Benchmark]
public void ParseFromStream() => subTests.ForEach(item => item.ParseFromStream(parser));
private class SubTest
{
private readonly Stream destinationStream;
private readonly Stream sourceStream;
private readonly ByteString data;
private readonly IMessage message;
public SubTest(ByteString data, IMessage message)
{
destinationStream = new MemoryStream(data.Length);
sourceStream = new MemoryStream(data.ToByteArray());
this.data = data;
this.message = message;
}
public void Reset() => destinationStream.Position = 0;
public void WriteToStream()
{
destinationStream.Position = 0;
message.WriteTo(destinationStream);
}
public void ToByteArray() => message.ToByteArray();
public void ParseFromByteString(MessageParser parser) => parser.ParseFrom(data);
public void ParseFromStream(MessageParser parser)
{
sourceStream.Position = 0;
parser.ParseFrom(sourceStream);
}
}
}
}

@ -0,0 +1,89 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2019 Google Inc. All rights reserved.
// https://github.com/protocolbuffers/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using Benchmarks;
using Google.Protobuf.Reflection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace Google.Protobuf.Benchmarks
{
/// <summary>
/// The configuration for a single serialization test, loaded from a dataset.
/// </summary>
public class SerializationConfig
{
private static readonly Dictionary<string, MessageParser> parsersByMessageName =
typeof(SerializationBenchmark).Assembly.GetTypes()
.Where(t => typeof(IMessage).IsAssignableFrom(t))
.ToDictionary(
t => ((MessageDescriptor) t.GetProperty("Descriptor", BindingFlags.Static | BindingFlags.Public).GetValue(null)).FullName,
t => ((MessageParser) t.GetProperty("Parser", BindingFlags.Static | BindingFlags.Public).GetValue(null)));
public MessageParser Parser { get; }
public IEnumerable<ByteString> Payloads { get; }
public string Name { get; }
public SerializationConfig(string resource)
{
var data = LoadData(resource);
var dataset = BenchmarkDataset.Parser.ParseFrom(data);
if (!parsersByMessageName.TryGetValue(dataset.MessageName, out var parser))
{
throw new ArgumentException($"No parser for message {dataset.MessageName} in this assembly");
}
Parser = parser;
Payloads = dataset.Payload;
Name = dataset.Name;
}
private static byte[] LoadData(string resource)
{
using (var stream = typeof(SerializationBenchmark).Assembly.GetManifestResourceStream($"Google.Protobuf.Benchmarks.{resource}"))
{
if (stream == null)
{
throw new ArgumentException($"Unable to load embedded resource {resource}");
}
var copy = new MemoryStream();
stream.CopyTo(copy);
return copy.ToArray();
}
}
public override string ToString() => Name;
}
}

@ -2,15 +2,17 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26114.2
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddressBook", "AddressBook\AddressBook.csproj", "{AFB63919-1E05-43B4-802A-8FB8C9B2F463}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AddressBook", "AddressBook\AddressBook.csproj", "{AFB63919-1E05-43B4-802A-8FB8C9B2F463}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Protobuf", "Google.Protobuf\Google.Protobuf.csproj", "{9B576380-726D-4142-8238-60A43AB0E35A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Protobuf", "Google.Protobuf\Google.Protobuf.csproj", "{9B576380-726D-4142-8238-60A43AB0E35A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Protobuf.Test", "Google.Protobuf.Test\Google.Protobuf.Test.csproj", "{580EB013-D3C7-4578-B845-015F4A3B0591}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Protobuf.Test", "Google.Protobuf.Test\Google.Protobuf.Test.csproj", "{580EB013-D3C7-4578-B845-015F4A3B0591}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Protobuf.Conformance", "Google.Protobuf.Conformance\Google.Protobuf.Conformance.csproj", "{DDDC055B-E185-4181-BAB0-072F0F984569}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Protobuf.Conformance", "Google.Protobuf.Conformance\Google.Protobuf.Conformance.csproj", "{DDDC055B-E185-4181-BAB0-072F0F984569}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Protobuf.JsonDump", "Google.Protobuf.JsonDump\Google.Protobuf.JsonDump.csproj", "{9695E08F-9829-497D-B95C-B38F28D48690}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Protobuf.JsonDump", "Google.Protobuf.JsonDump\Google.Protobuf.JsonDump.csproj", "{9695E08F-9829-497D-B95C-B38F28D48690}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Protobuf.Benchmarks", "Google.Protobuf.Benchmarks\Google.Protobuf.Benchmarks.csproj", "{D25E4804-4DEA-45AB-9F8C-BA4DBD8E5A07}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -38,8 +40,15 @@ Global
{9695E08F-9829-497D-B95C-B38F28D48690}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9695E08F-9829-497D-B95C-B38F28D48690}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9695E08F-9829-497D-B95C-B38F28D48690}.Release|Any CPU.Build.0 = Release|Any CPU
{D25E4804-4DEA-45AB-9F8C-BA4DBD8E5A07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D25E4804-4DEA-45AB-9F8C-BA4DBD8E5A07}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D25E4804-4DEA-45AB-9F8C-BA4DBD8E5A07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D25E4804-4DEA-45AB-9F8C-BA4DBD8E5A07}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7B06C87B-83E1-4F5F-A0DD-6E9AFAC03DAC}
EndGlobalSection
EndGlobal

Loading…
Cancel
Save