From c3f179311ec003451bcb94b648ad34d290e6a3c5 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 6 Apr 2018 13:36:35 +0200 Subject: [PATCH 01/10] initial Benchmarks solution structure: .NET Standard 2.0 Definitions + platform specific Runner --- .gitignore | 2 ++ src/benchmarks/Benchmarks.sln | 31 +++++++++++++++++++ src/benchmarks/Definitions/Definitions.csproj | 7 +++++ src/benchmarks/Runner/Program.cs | 12 +++++++ src/benchmarks/Runner/Runner.csproj | 12 +++++++ 5 files changed, 64 insertions(+) create mode 100644 src/benchmarks/Benchmarks.sln create mode 100644 src/benchmarks/Definitions/Definitions.csproj create mode 100644 src/benchmarks/Runner/Program.cs create mode 100644 src/benchmarks/Runner/Runner.csproj diff --git a/.gitignore b/.gitignore index a90b6637143..8e0bce4abb3 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ **/__pycache__/ *.pyc + +**/.vs/** diff --git a/src/benchmarks/Benchmarks.sln b/src/benchmarks/Benchmarks.sln new file mode 100644 index 00000000000..14cd7f37cbc --- /dev/null +++ b/src/benchmarks/Benchmarks.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27428.2011 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Runner", "Runner\Runner.csproj", "{D99F63AE-3154-4F13-9424-FA5F9D032D1D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Definitions", "Definitions\Definitions.csproj", "{5DB7E599-9242-4A8F-BA0F-DB691155E733}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D99F63AE-3154-4F13-9424-FA5F9D032D1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D99F63AE-3154-4F13-9424-FA5F9D032D1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D99F63AE-3154-4F13-9424-FA5F9D032D1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D99F63AE-3154-4F13-9424-FA5F9D032D1D}.Release|Any CPU.Build.0 = Release|Any CPU + {5DB7E599-9242-4A8F-BA0F-DB691155E733}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5DB7E599-9242-4A8F-BA0F-DB691155E733}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5DB7E599-9242-4A8F-BA0F-DB691155E733}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5DB7E599-9242-4A8F-BA0F-DB691155E733}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {59625A56-73BB-47B5-8B66-02F6918E41BD} + EndGlobalSection +EndGlobal diff --git a/src/benchmarks/Definitions/Definitions.csproj b/src/benchmarks/Definitions/Definitions.csproj new file mode 100644 index 00000000000..9f5c4f4abb6 --- /dev/null +++ b/src/benchmarks/Definitions/Definitions.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/src/benchmarks/Runner/Program.cs b/src/benchmarks/Runner/Program.cs new file mode 100644 index 00000000000..1667ffbbb55 --- /dev/null +++ b/src/benchmarks/Runner/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace Runner +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + } + } +} diff --git a/src/benchmarks/Runner/Runner.csproj b/src/benchmarks/Runner/Runner.csproj new file mode 100644 index 00000000000..2ac557c541b --- /dev/null +++ b/src/benchmarks/Runner/Runner.csproj @@ -0,0 +1,12 @@ + + + + Exe + net461;netcoreapp2.0;netcoreapp2.1 + + + + + + + From 0be66cc4be5c5705a260159274794d84bcc9c72e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 6 Apr 2018 13:45:49 +0200 Subject: [PATCH 02/10] add NuGet.Config file with BenchmarkDotNet CI feed --- src/benchmarks/NuGet.Config | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/benchmarks/NuGet.Config diff --git a/src/benchmarks/NuGet.Config b/src/benchmarks/NuGet.Config new file mode 100644 index 00000000000..af68a668393 --- /dev/null +++ b/src/benchmarks/NuGet.Config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file From 4edf48750915272a92096c62116b6c50ae9c0c02 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 6 Apr 2018 13:50:46 +0200 Subject: [PATCH 03/10] introduce serializers benchmarks --- src/benchmarks/Definitions/Definitions.csproj | 13 + .../Serializers/Binary_FromStream.cs | 97 ++++++ .../Serializers/Binary_ToStream.cs | 54 +++ .../Definitions/Serializers/DataGenerator.cs | 310 ++++++++++++++++++ .../Helpers/DateTimeOffsetSurrogate.cs | 22 ++ .../Serializers/Helpers/Workarounds.cs | 49 +++ .../Serializers/Json_FromStream.cs | 109 ++++++ .../Serializers/Json_FromString.cs | 30 ++ .../Definitions/Serializers/Json_ToStream.cs | 65 ++++ .../Definitions/Serializers/Json_ToString.cs | 23 ++ .../Definitions/Serializers/Xml_FromStream.cs | 56 ++++ .../Definitions/Serializers/Xml_ToStream.cs | 77 +++++ 12 files changed, 905 insertions(+) create mode 100644 src/benchmarks/Definitions/Serializers/Binary_FromStream.cs create mode 100644 src/benchmarks/Definitions/Serializers/Binary_ToStream.cs create mode 100644 src/benchmarks/Definitions/Serializers/DataGenerator.cs create mode 100644 src/benchmarks/Definitions/Serializers/Helpers/DateTimeOffsetSurrogate.cs create mode 100644 src/benchmarks/Definitions/Serializers/Helpers/Workarounds.cs create mode 100644 src/benchmarks/Definitions/Serializers/Json_FromStream.cs create mode 100644 src/benchmarks/Definitions/Serializers/Json_FromString.cs create mode 100644 src/benchmarks/Definitions/Serializers/Json_ToStream.cs create mode 100644 src/benchmarks/Definitions/Serializers/Json_ToString.cs create mode 100644 src/benchmarks/Definitions/Serializers/Xml_FromStream.cs create mode 100644 src/benchmarks/Definitions/Serializers/Xml_ToStream.cs diff --git a/src/benchmarks/Definitions/Definitions.csproj b/src/benchmarks/Definitions/Definitions.csproj index 9f5c4f4abb6..aeb0417ff32 100644 --- a/src/benchmarks/Definitions/Definitions.csproj +++ b/src/benchmarks/Definitions/Definitions.csproj @@ -4,4 +4,17 @@ netstandard2.0 + + + + + + + + + + + + + diff --git a/src/benchmarks/Definitions/Serializers/Binary_FromStream.cs b/src/benchmarks/Definitions/Serializers/Binary_FromStream.cs new file mode 100644 index 00000000000..caea09c1bb9 --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/Binary_FromStream.cs @@ -0,0 +1,97 @@ +using System; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using BenchmarkDotNet.Attributes; +using Definitions.Serializers.Helpers; + +namespace Definitions.Serializers +{ + public class Binary_FromStream where T : IVerifiable + { + private readonly T value; + private readonly MemoryStream memoryStream; + private readonly BinaryFormatter binaryFormatter; + + public Binary_FromStream() + { + value = DataGenerator.Generate(); + + // the stream is pre-allocated, we don't want the benchmarks to include stream allocaton cost + memoryStream = new MemoryStream(capacity: short.MaxValue); + binaryFormatter = new BinaryFormatter(); + + ProtoBuf.Meta.RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate)); // https://stackoverflow.com/a/7046868 + } + + [IterationSetup(Target = nameof(BinaryFormatter_))] + public void SetupBinaryFormatter() + { + memoryStream.Position = 0; + binaryFormatter.Serialize(memoryStream, value); + } + + [IterationSetup(Target = nameof(ProtoBuffNet))] + public void SetupProtoBuffNet() + { + memoryStream.Position = 0; + ProtoBuf.Serializer.Serialize(memoryStream, value); + } + + [IterationSetup(Target = nameof(ZeroFormatter_Naive) + "," + nameof(ZeroFormatter_Real))] + public void SetupZeroFormatter_() + { + memoryStream.Position = 0; + ZeroFormatter.ZeroFormatterSerializer.Serialize(memoryStream, value); + } + + [IterationSetup(Target = nameof(MessagePack_))] + public void SetupMessagePack() + { + memoryStream.Position = 0; + MessagePack.MessagePackSerializer.Serialize(memoryStream, value); + } + + [Benchmark(Description = nameof(BinaryFormatter))] + public T BinaryFormatter_() + { + memoryStream.Position = 0; + return (T)binaryFormatter.Deserialize(memoryStream); + } + + [Benchmark(Description = "protobuf-net")] + public T ProtoBuffNet() + { + memoryStream.Position = 0; + return ProtoBuf.Serializer.Deserialize(memoryStream); + } + + [Benchmark(Description = "ZeroFormatter_Naive")] + public T ZeroFormatter_Naive() + { + memoryStream.Position = 0; + return ZeroFormatter.ZeroFormatterSerializer.Deserialize(memoryStream); + } + + /// + /// ZeroFormatter requires all properties to be virtual + /// they are deserialized for real when they are used for the first time + /// if we don't touch the properites, they are not being deserialized and the result is skewed + /// + [Benchmark(Description = "ZeroFormatter_Real")] + public long ZeroFormatter_Real() + { + memoryStream.Position = 0; + + var deserialized = ZeroFormatter.ZeroFormatterSerializer.Deserialize(memoryStream); + + return deserialized.TouchEveryProperty(); + } + + [Benchmark(Description = "MessagePack")] + public T MessagePack_() + { + memoryStream.Position = 0; + return MessagePack.MessagePackSerializer.Deserialize(memoryStream); + } + } +} diff --git a/src/benchmarks/Definitions/Serializers/Binary_ToStream.cs b/src/benchmarks/Definitions/Serializers/Binary_ToStream.cs new file mode 100644 index 00000000000..1d9fa94a00b --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/Binary_ToStream.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using BenchmarkDotNet.Attributes; +using Definitions.Serializers.Helpers; + +namespace Definitions.Serializers +{ + public class Binary_ToStream + { + private readonly T value; + private readonly MemoryStream memoryStream; + private readonly BinaryFormatter binaryFormatter; + + public Binary_ToStream() + { + value = DataGenerator.Generate(); + + // the stream is pre-allocated, we don't want the benchmarks to include stream allocaton cost + memoryStream = new MemoryStream(capacity: short.MaxValue); + binaryFormatter = new BinaryFormatter(); + + ProtoBuf.Meta.RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate)); // https://stackoverflow.com/a/7046868 + } + + [Benchmark(Description = nameof(BinaryFormatter))] + public void BinaryFormatter_() + { + memoryStream.Position = 0; + binaryFormatter.Serialize(memoryStream, value); + } + + [Benchmark(Description = "protobuf-net")] + public void ProtoBuffNet() + { + memoryStream.Position = 0; + ProtoBuf.Serializer.Serialize(memoryStream, value); + } + + [Benchmark(Description = "ZeroFormatter")] + public void ZeroFormatter_() + { + memoryStream.Position = 0; + ZeroFormatter.ZeroFormatterSerializer.Serialize(memoryStream, value); + } + + [Benchmark(Description = "MessagePack")] + public void MessagePack_() + { + memoryStream.Position = 0; + MessagePack.MessagePackSerializer.Serialize(memoryStream, value); + } + } +} diff --git a/src/benchmarks/Definitions/Serializers/DataGenerator.cs b/src/benchmarks/Definitions/Serializers/DataGenerator.cs new file mode 100644 index 00000000000..618634b2797 --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/DataGenerator.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MessagePack; +using ProtoBuf; +using ZeroFormatter; +#if SGEN +// the new SGEN tool fails to load some of the dependencies, so we need to replace the problematic dependencies for this particular build configuration +// see https://github.com/dotnet/corefx/issues/27281#issuecomment-367449130 fore more +using Benchmarks.Serializers.Helpers; +#else + +#endif + +namespace Definitions.Serializers +{ + internal static class DataGenerator + { + internal static T Generate() + { + if (typeof(T) == typeof(LoginViewModel)) + return (T)(object)CreateLoginViewModel(); + if (typeof(T) == typeof(Location)) + return (T)(object)CreateLocation(); + if (typeof(T) == typeof(IndexViewModel)) + return (T)(object)CreateIndexViewModel(); + if (typeof(T) == typeof(MyEventsListerViewModel)) + return (T)(object)CreateMyEventsListerViewModel(); + + throw new NotImplementedException(); + } + + private static LoginViewModel CreateLoginViewModel() + => new LoginViewModel + { + Email = "name.familyname@not.com", + Password = "abcdefgh123456!@", + RememberMe = true + }; + + private static Location CreateLocation() + => new Location + { + Id = 1234, + Address1 = "The Street Name", + Address2 = "20/11", + City = "The City", + State = "The State", + PostalCode = "abc-12", + Name = "Nonexisting", + PhoneNumber = "+0 11 222 333 44", + Country = "The Greatest" + }; + + private static IndexViewModel CreateIndexViewModel() + => new IndexViewModel + { + IsNewAccount = false, + FeaturedCampaign = new CampaignSummaryViewModel + { + Description = "Very nice campaing", + Headline = "The Headline", + Id = 234235, + OrganizationName = "The Company XYZ", + ImageUrl = "https://www.dotnetfoundation.org/theme/img/carousel/foundation-diagram-content.png", + Title = "Promoting Open Source" + }, + ActiveOrUpcomingEvents = Enumerable.Repeat( + new ActiveOrUpcomingEvent + { + Id = 10, + CampaignManagedOrganizerName = "Name FamiltyName", + CampaignName = "The very new campaing", + Description = "The .NET Foundation works with Microsoft and the broader industry to increase the exposure of open source projects in the .NET community and the .NET Foundation. The .NET Foundation provides access to these resources to projects and looks to promote the activities of our communities.", + EndDate = DateTime.UtcNow.AddYears(1), + Name = "Just a name", + ImageUrl = "https://www.dotnetfoundation.org/theme/img/carousel/foundation-diagram-content.png", + StartDate = DateTime.UtcNow + }, + count: 20).ToList() + }; + + private static MyEventsListerViewModel CreateMyEventsListerViewModel() + => new MyEventsListerViewModel + { + CurrentEvents = Enumerable.Repeat(CreateMyEventsListerItem(), 3).ToList(), + FutureEvents = Enumerable.Repeat(CreateMyEventsListerItem(), 9).ToList(), + PastEvents = Enumerable.Repeat(CreateMyEventsListerItem(), 60).ToList() // usually there is a lot of historical data + }; + + private static MyEventsListerItem CreateMyEventsListerItem() + => new MyEventsListerItem + { + Campaign = "A very nice campaing", + EndDate = DateTime.UtcNow.AddDays(7), + EventId = 321, + EventName = "wonderful name", + Organization = "Local Animal Shelter", + StartDate = DateTime.UtcNow.AddDays(-7), + TimeZone = TimeZoneInfo.Utc.DisplayName, + VolunteerCount = 15, + Tasks = Enumerable.Repeat( + new MyEventsListerItemTask + { + StartDate = DateTime.UtcNow, + EndDate = DateTime.UtcNow.AddDays(1), + Name = "A very nice task to have" + }, 4).ToList() + }; + } + + /// + /// ZeroFormatter requires all properties to be virtual + /// they are deserialized for real when they are used for the first time + /// if we don't touch the properites, they are not being deserialized and the result is skewed + /// + public interface IVerifiable + { + long TouchEveryProperty(); + } + + // the view models come from a real world app called "AllReady" + [Serializable] + [ProtoContract] + [ZeroFormattable] + [MessagePackObject] + public class LoginViewModel : IVerifiable + { + [ProtoMember(1)] [Index(0)] [Key(0)] public virtual string Email { get; set; } + [ProtoMember(2)] [Index(1)] [Key(1)] public virtual string Password { get; set; } + [ProtoMember(3)] [Index(2)] [Key(2)] public virtual bool RememberMe { get; set; } + + public long TouchEveryProperty() => Email.Length + Password.Length + Convert.ToInt32(RememberMe); + } + + [Serializable] + [ProtoContract] + [ZeroFormattable] + [MessagePackObject] + public class Location : IVerifiable + { + [ProtoMember(1)] [Index(0)] [Key(0)] public virtual int Id { get; set; } + [ProtoMember(2)] [Index(1)] [Key(1)] public virtual string Address1 { get; set; } + [ProtoMember(3)] [Index(2)] [Key(2)] public virtual string Address2 { get; set; } + [ProtoMember(4)] [Index(3)] [Key(3)] public virtual string City { get; set; } + [ProtoMember(5)] [Index(4)] [Key(4)] public virtual string State { get; set; } + [ProtoMember(6)] [Index(5)] [Key(5)] public virtual string PostalCode { get; set; } + [ProtoMember(7)] [Index(6)] [Key(6)] public virtual string Name { get; set; } + [ProtoMember(8)] [Index(7)] [Key(7)] public virtual string PhoneNumber { get; set; } + [ProtoMember(9)] [Index(8)] [Key(8)] public virtual string Country { get; set; } + + public long TouchEveryProperty() => Id + Address1.Length + Address2.Length + City.Length + State.Length + PostalCode.Length + Name.Length + PhoneNumber.Length + Country.Length; + } + + [Serializable] + [ProtoContract] + [ZeroFormattable] + [MessagePackObject] + public class ActiveOrUpcomingCampaign : IVerifiable + { + [ProtoMember(1)] [Index(0)] [Key(0)] public virtual int Id { get; set; } + [ProtoMember(2)] [Index(1)] [Key(1)] public virtual string ImageUrl { get; set; } + [ProtoMember(3)] [Index(2)] [Key(2)] public virtual string Name { get; set; } + [ProtoMember(4)] [Index(3)] [Key(3)] public virtual string Description { get; set; } + [ProtoMember(5)] [Index(4)] [Key(4)] public virtual DateTimeOffset StartDate { get; set; } + [ProtoMember(6)] [Index(5)] [Key(5)] public virtual DateTimeOffset EndDate { get; set; } + + public long TouchEveryProperty() => Id + ImageUrl.Length + Name.Length + Description.Length + StartDate.Ticks + EndDate.Ticks; + } + + [Serializable] + [ProtoContract] + [ZeroFormattable] + [MessagePackObject] + public class ActiveOrUpcomingEvent : IVerifiable + { + [ProtoMember(1)] [Index(0)] [Key(0)] public virtual int Id { get; set; } + [ProtoMember(2)] [Index(1)] [Key(1)] public virtual string ImageUrl { get; set; } + [ProtoMember(3)] [Index(2)] [Key(2)] public virtual string Name { get; set; } + [ProtoMember(4)] [Index(3)] [Key(3)] public virtual string CampaignName { get; set; } + [ProtoMember(5)] [Index(4)] [Key(4)] public virtual string CampaignManagedOrganizerName { get; set; } + [ProtoMember(6)] [Index(5)] [Key(5)] public virtual string Description { get; set; } + [ProtoMember(7)] [Index(6)] [Key(6)] public virtual DateTimeOffset StartDate { get; set; } + [ProtoMember(8)] [Index(7)] [Key(7)] public virtual DateTimeOffset EndDate { get; set; } + + public long TouchEveryProperty() => Id + ImageUrl.Length + Name.Length + CampaignName.Length + CampaignManagedOrganizerName.Length + Description.Length + StartDate.Ticks + EndDate.Ticks; + } + + [Serializable] + [ProtoContract] + [ZeroFormattable] + [MessagePackObject] + public class CampaignSummaryViewModel : IVerifiable + { + [ProtoMember(1)] [Index(0)] [Key(0)] public virtual int Id { get; set; } + [ProtoMember(2)] [Index(1)] [Key(1)] public virtual string Title { get; set; } + [ProtoMember(3)] [Index(2)] [Key(2)] public virtual string Description { get; set; } + [ProtoMember(4)] [Index(3)] [Key(3)] public virtual string ImageUrl { get; set; } + [ProtoMember(5)] [Index(4)] [Key(4)] public virtual string OrganizationName { get; set; } + [ProtoMember(6)] [Index(5)] [Key(5)] public virtual string Headline { get; set; } + + public long TouchEveryProperty() => Id + Title.Length + Description.Length + ImageUrl.Length + OrganizationName.Length + Headline.Length; + } + + [Serializable] + [ProtoContract] + [ZeroFormattable] + [MessagePackObject] + public class IndexViewModel : IVerifiable + { + [ProtoMember(1)] [Index(0)] [Key(0)] public virtual List ActiveOrUpcomingEvents { get; set; } + [ProtoMember(2)] [Index(1)] [Key(1)] public virtual CampaignSummaryViewModel FeaturedCampaign { get; set; } + [ProtoMember(3)] [Index(2)] [Key(2)] public virtual bool IsNewAccount { get; set; } + [IgnoreFormat] [IgnoreMember] public bool HasFeaturedCampaign => FeaturedCampaign != null; + + public long TouchEveryProperty() + { + long result = FeaturedCampaign.TouchEveryProperty() + Convert.ToInt32(IsNewAccount); + + for (int i = 0; i < ActiveOrUpcomingEvents.Count; i++) // no LINQ here to prevent from skewing allocations results + result += ActiveOrUpcomingEvents[i].TouchEveryProperty(); + + return result; + } + } + + [Serializable] + [ProtoContract] + [ZeroFormattable] + [MessagePackObject] + public class MyEventsListerViewModel : IVerifiable + { + // the orginal type defined these fields as IEnumerable, + // but XmlSerializer failed to serialize them with "cannot serialize member because it is an interface" error + [ProtoMember(1)] [Index(0)] [Key(0)] public virtual List CurrentEvents { get; set; } = new List(); + [ProtoMember(2)] [Index(1)] [Key(1)] public virtual List FutureEvents { get; set; } = new List(); + [ProtoMember(3)] [Index(2)] [Key(2)] public virtual List PastEvents { get; set; } = new List(); + + public long TouchEveryProperty() + { + long result = 0; + + // no LINQ here to prevent from skewing allocations results + for (int i = 0; i < CurrentEvents.Count; i++) result += CurrentEvents[i].TouchEveryProperty(); + for (int i = 0; i < FutureEvents.Count; i++) result += FutureEvents[i].TouchEveryProperty(); + for (int i = 0; i < PastEvents.Count; i++) result += PastEvents[i].TouchEveryProperty(); + + return result; + } + } + + [Serializable] + [ProtoContract] + [ZeroFormattable] + [MessagePackObject] + public class MyEventsListerItem : IVerifiable + { + [ProtoMember(1)] [Index(0)] [Key(0)] public virtual int EventId { get; set; } + [ProtoMember(2)] [Index(1)] [Key(1)] public virtual string EventName { get; set; } + [ProtoMember(3)] [Index(2)] [Key(2)] public virtual DateTimeOffset StartDate { get; set; } + [ProtoMember(4)] [Index(3)] [Key(3)] public virtual DateTimeOffset EndDate { get; set; } + [ProtoMember(5)] [Index(4)] [Key(4)] public virtual string TimeZone { get; set; } + [ProtoMember(6)] [Index(5)] [Key(5)] public virtual string Campaign { get; set; } + [ProtoMember(7)] [Index(6)] [Key(6)] public virtual string Organization { get; set; } + [ProtoMember(8)] [Index(7)] [Key(7)] public virtual int VolunteerCount { get; set; } + + [ProtoMember(9)] [Index(8)] [Key(8)] public virtual List Tasks { get; set; } = new List(); + + public long TouchEveryProperty() + { + long result = EventId + EventName.Length + StartDate.Ticks + EndDate.Ticks + TimeZone.Length + Campaign.Length + Organization.Length + VolunteerCount; + + for (int i = 0; i < Tasks.Count; i++) // no LINQ here to prevent from skewing allocations results + result += Tasks[i].TouchEveryProperty(); + + return result; + } + } + + [Serializable] + [ProtoContract] + [ZeroFormattable] + [MessagePackObject] + public class MyEventsListerItemTask : IVerifiable + { + [ProtoMember(1)] [Index(0)] [Key(0)] public virtual string Name { get; set; } + [ProtoMember(2)] [Index(1)] [Key(1)] public virtual DateTimeOffset? StartDate { get; set; } + [ProtoMember(3)] [Index(2)] [Key(2)] public virtual DateTimeOffset? EndDate { get; set; } + + [IgnoreFormat] + [IgnoreMember] + public string FormattedDate + { + get + { + if (!StartDate.HasValue || !EndDate.HasValue) + { + return null; + } + + var startDateString = string.Format("{0:g}", StartDate.Value); + var endDateString = string.Format("{0:g}", EndDate.Value); + + return string.Format($"From {startDateString} to {endDateString}"); + } + } + + public long TouchEveryProperty() => Name.Length + StartDate.Value.Ticks + EndDate.Value.Ticks; + } +} \ No newline at end of file diff --git a/src/benchmarks/Definitions/Serializers/Helpers/DateTimeOffsetSurrogate.cs b/src/benchmarks/Definitions/Serializers/Helpers/DateTimeOffsetSurrogate.cs new file mode 100644 index 00000000000..387b8606cd8 --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/Helpers/DateTimeOffsetSurrogate.cs @@ -0,0 +1,22 @@ +using System; +using ProtoBuf; + +namespace Definitions.Serializers.Helpers +{ + [ProtoContract] + public class DateTimeOffsetSurrogate + { + [ProtoMember(1)] + public string DateTimeString { get; set; } + + public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value) + { + return new DateTimeOffsetSurrogate { DateTimeString = value.ToString("u") }; + } + + public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value) + { + return DateTimeOffset.Parse(value.DateTimeString); + } + } +} diff --git a/src/benchmarks/Definitions/Serializers/Helpers/Workarounds.cs b/src/benchmarks/Definitions/Serializers/Helpers/Workarounds.cs new file mode 100644 index 00000000000..cde45d2ebdd --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/Helpers/Workarounds.cs @@ -0,0 +1,49 @@ +#if SGEN +// the new SGEN tool fails to load some of the dependencies, so we need to replace the problematic dependencies for this particular build configuration +using System; +using System.Collections.Generic; +using System.Text; + +namespace Benchmarks.Serializers.Helpers +{ + public class ProtoContractAttribute : Attribute + { + public ProtoContractAttribute() { } + } + + public class ProtoMemberAttribute : Attribute + { + public ProtoMemberAttribute(int tag) { } + } + + public class ZeroFormattableAttribute : Attribute + { + public ZeroFormattableAttribute() { } + } + + public class MessagePackObjectAttribute : Attribute + { + public MessagePackObjectAttribute() { } + } + + public class IndexAttribute : Attribute + { + public IndexAttribute(int index) { } + } + + public class KeyAttribute : Attribute + { + public KeyAttribute(int key) { } + } + + public class IgnoreFormatAttribute : Attribute + { + public IgnoreFormatAttribute() { } + } + + public class IgnoreMemberAttribute : Attribute + { + public IgnoreMemberAttribute() { } + } +} +#endif \ No newline at end of file diff --git a/src/benchmarks/Definitions/Serializers/Json_FromStream.cs b/src/benchmarks/Definitions/Serializers/Json_FromStream.cs new file mode 100644 index 00000000000..d768fdb17be --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/Json_FromStream.cs @@ -0,0 +1,109 @@ +using System.IO; +using System.Runtime.Serialization.Json; +using System.Text; +using BenchmarkDotNet.Attributes; + +namespace Definitions.Serializers +{ + public class Json_FromStream + { + private readonly T value; + + private readonly MemoryStream memoryStream; + + private DataContractJsonSerializer dataContractJsonSerializer; + private Newtonsoft.Json.JsonSerializer newtonSoftJsonSerializer; + + public Json_FromStream() + { + value = DataGenerator.Generate(); + + // the stream is pre-allocated, we don't want the benchmarks to include stream allocaton cost + memoryStream = new MemoryStream(capacity: short.MaxValue); + + dataContractJsonSerializer = new DataContractJsonSerializer(typeof(T)); + newtonSoftJsonSerializer = new Newtonsoft.Json.JsonSerializer(); + } + + [IterationSetup(Target = nameof(Jil_))] + public void SetupJil_() + { + memoryStream.Position = 0; + + using (var writer = new StreamWriter(memoryStream, Encoding.UTF8, short.MaxValue, leaveOpen: true)) + { + Jil.JSON.Serialize(value, writer); + writer.Flush(); + } + } + + [IterationSetup(Target = nameof(JsonNet_))] + public void SetupJsonNet_() + { + memoryStream.Position = 0; + + using (var writer = new StreamWriter(memoryStream, Encoding.UTF8, short.MaxValue, leaveOpen: true)) + { + newtonSoftJsonSerializer.Serialize(writer, value); + writer.Flush(); + } + } + + [IterationSetup(Target = nameof(Utf8Json_))] + public void SetupUtf8Json_() + { + memoryStream.Position = 0; + Utf8Json.JsonSerializer.Serialize(memoryStream, value); + } + + [IterationSetup(Target = nameof(DataContractJsonSerializer_))] + public void SetupDataContractJsonSerializer_() + { + memoryStream.Position = 0; + dataContractJsonSerializer.WriteObject(memoryStream, value); + } + + [Benchmark(Description = "Jil")] + public T Jil_() + { + memoryStream.Position = 0; + + using (var reader = CreateNonClosingReaderWithDefaultSizes()) + return Jil.JSON.Deserialize(reader); + } + + [Benchmark(Description = "JSON.NET")] + public T JsonNet_() + { + memoryStream.Position = 0; + + using (var reader = CreateNonClosingReaderWithDefaultSizes()) + return (T)newtonSoftJsonSerializer.Deserialize(reader, typeof(T)); + } + + [Benchmark(Description = "Utf8Json")] + public T Utf8Json_() + { + memoryStream.Position = 0; + return Utf8Json.JsonSerializer.Deserialize(memoryStream); + } + + [Benchmark(Description = "DataContractJsonSerializer")] + public T DataContractJsonSerializer_() + { + memoryStream.Position = 0; + return (T)dataContractJsonSerializer.ReadObject(memoryStream); + } + + private StreamReader CreateNonClosingReaderWithDefaultSizes() + => new StreamReader( + memoryStream, + Encoding.UTF8, + true, // default is true https://github.com/dotnet/corefx/blob/708e4537d8944199af7d580def0d97a030be98c7/src/Common/src/CoreLib/System/IO/StreamReader.cs#L98 + 1024, // default buffer size from CoreFX https://github.com/dotnet/corefx/blob/708e4537d8944199af7d580def0d97a030be98c7/src/Common/src/CoreLib/System/IO/StreamReader.cs#L27 + leaveOpen: true); // we want to reuse the same string in the benchmarks to make sure that cost of allocating stream is not included in the benchmarks + + [GlobalCleanup] + public void Cleanup() => memoryStream.Dispose(); + } +} diff --git a/src/benchmarks/Definitions/Serializers/Json_FromString.cs b/src/benchmarks/Definitions/Serializers/Json_FromString.cs new file mode 100644 index 00000000000..8c647bb061b --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/Json_FromString.cs @@ -0,0 +1,30 @@ +using BenchmarkDotNet.Attributes; + +namespace Definitions.Serializers +{ + public class Json_FromString + { + private readonly T value; + private string serialized; + + public Json_FromString() => value = DataGenerator.Generate(); + + [IterationSetup(Target = nameof(Jil_))] + public void SerializeJil() => serialized = Jil.JSON.Serialize(value); + + [IterationSetup(Target = nameof(JsonNet_))] + public void SerializeJsonNet() => serialized = Newtonsoft.Json.JsonConvert.SerializeObject(value); + + [IterationSetup(Target = nameof(Utf8Json_))] + public void SerializeUtf8Json_() => serialized = Utf8Json.JsonSerializer.ToJsonString(value); + + [Benchmark(Description = "Jil")] + public T Jil_() => Jil.JSON.Deserialize(serialized); + + [Benchmark(Description = "JSON.NET")] + public T JsonNet_() => Newtonsoft.Json.JsonConvert.DeserializeObject(serialized); + + [Benchmark(Description = "Utf8Json")] + public T Utf8Json_() => Utf8Json.JsonSerializer.Deserialize(serialized); + } +} diff --git a/src/benchmarks/Definitions/Serializers/Json_ToStream.cs b/src/benchmarks/Definitions/Serializers/Json_ToStream.cs new file mode 100644 index 00000000000..6ec822f19e7 --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/Json_ToStream.cs @@ -0,0 +1,65 @@ +using System.IO; +using System.Runtime.Serialization.Json; +using System.Text; +using BenchmarkDotNet.Attributes; + +namespace Definitions.Serializers +{ + public class Json_ToStream + { + private readonly T value; + + private readonly MemoryStream memoryStream; + private readonly StreamWriter streamWriter; + + private DataContractJsonSerializer dataContractJsonSerializer; + private Newtonsoft.Json.JsonSerializer newtonSoftJsonSerializer; + + public Json_ToStream() + { + value = DataGenerator.Generate(); + + // the stream is pre-allocated, we don't want the benchmarks to include stream allocaton cost + memoryStream = new MemoryStream(capacity: short.MaxValue); + streamWriter = new StreamWriter(memoryStream, Encoding.UTF8); + + dataContractJsonSerializer = new DataContractJsonSerializer(typeof(T)); + newtonSoftJsonSerializer = new Newtonsoft.Json.JsonSerializer(); + } + + [Benchmark(Description = "Jil")] + public void Jil_() + { + memoryStream.Position = 0; + Jil.JSON.Serialize(value, streamWriter); + } + + [Benchmark(Description = "JSON.NET")] + public void JsonNet_() + { + memoryStream.Position = 0; + newtonSoftJsonSerializer.Serialize(streamWriter, value); + } + + [Benchmark(Description = "Utf8Json")] + public void Utf8Json_() + { + memoryStream.Position = 0; + Utf8Json.JsonSerializer.Serialize(memoryStream, value); + } + + [Benchmark(Description = "DataContractJsonSerializer")] + public void DataContractJsonSerializer_() + { + memoryStream.Position = 0; + dataContractJsonSerializer.WriteObject(memoryStream, value); + } + + [GlobalCleanup] + public void Cleanup() + { + streamWriter.Dispose(); + memoryStream.Dispose(); + } + } +} diff --git a/src/benchmarks/Definitions/Serializers/Json_ToString.cs b/src/benchmarks/Definitions/Serializers/Json_ToString.cs new file mode 100644 index 00000000000..a0846bc2c6f --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/Json_ToString.cs @@ -0,0 +1,23 @@ +using BenchmarkDotNet.Attributes; + +namespace Definitions.Serializers +{ + public class Json_ToString + { + private readonly T value; + + public Json_ToString() => value = DataGenerator.Generate(); + + [Benchmark(Description = "Jil")] + public string Jil_() => Jil.JSON.Serialize(value); + + [Benchmark(Description = "JSON.NET")] + public string JsonNet_() => Newtonsoft.Json.JsonConvert.SerializeObject(value); + + [Benchmark(Description = "Utf8Json")] + public string Utf8Json_() => Utf8Json.JsonSerializer.ToJsonString(value); + + // DataContractJsonSerializer does not provide an API to serialize to string + // so it's not included here (apples vs apples thing) + } +} diff --git a/src/benchmarks/Definitions/Serializers/Xml_FromStream.cs b/src/benchmarks/Definitions/Serializers/Xml_FromStream.cs new file mode 100644 index 00000000000..6de8ea95fb2 --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/Xml_FromStream.cs @@ -0,0 +1,56 @@ +using System.IO; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using BenchmarkDotNet.Attributes; + +namespace Definitions.Serializers +{ + public class Xml_FromStream + { + private readonly T value; + private readonly XmlSerializer xmlSerializer; + private readonly DataContractSerializer dataContractSerializer; + private readonly MemoryStream memoryStream; + + public Xml_FromStream() + { + value = DataGenerator.Generate(); + xmlSerializer = new XmlSerializer(typeof(T)); + dataContractSerializer = new DataContractSerializer(typeof(T)); + memoryStream = new MemoryStream(capacity: short.MaxValue); + } + + [IterationSetup(Target = nameof(XmlSerializer_))] + public void SetupXmlSerializer() + { + memoryStream.Position = 0; + xmlSerializer.Serialize(memoryStream, value); + } + + [IterationSetup(Target = nameof(DataContractSerializer_))] + public void SetupDataContractSerializer() + { + memoryStream.Position = 0; + dataContractSerializer.WriteObject(memoryStream, value); + } + + [Benchmark(Description = nameof(XmlSerializer))] + public T XmlSerializer_() + { + memoryStream.Position = 0; + return (T)xmlSerializer.Deserialize(memoryStream); + } + + [Benchmark(Description = nameof(DataContractSerializer))] + public T DataContractSerializer_() + { + memoryStream.Position = 0; + return (T)dataContractSerializer.ReadObject(memoryStream); + } + + // YAXSerializer is not included in the benchmarks because it does not allow to deserialize from stream (only from file and string) + + [GlobalCleanup] + public void Cleanup() => memoryStream.Dispose(); + } +} diff --git a/src/benchmarks/Definitions/Serializers/Xml_ToStream.cs b/src/benchmarks/Definitions/Serializers/Xml_ToStream.cs new file mode 100644 index 00000000000..087da8bf206 --- /dev/null +++ b/src/benchmarks/Definitions/Serializers/Xml_ToStream.cs @@ -0,0 +1,77 @@ +using System.IO; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Toolchains.CsProj; +using BenchmarkDotNet.Toolchains.DotNetCli; + +namespace Definitions.Serializers +{ + //[Config(typeof(SgenConfig))] // currently blocked https://github.com/dotnet/corefx/issues/27281#issuecomment-367533728 + public class Xml_ToStream + { + private readonly T value; + private readonly XmlSerializer xmlSerializer; + private readonly DataContractSerializer dataContractSerializer; + private readonly MemoryStream memoryStream; + + public Xml_ToStream() + { + value = DataGenerator.Generate(); + xmlSerializer = new XmlSerializer(typeof(T)); + dataContractSerializer = new DataContractSerializer(typeof(T)); + memoryStream = new MemoryStream(capacity: short.MaxValue); + +#if SGEN + // we need to give some hints to the SGEN tool ;) + if (typeof(T) == typeof(LoginViewModel)) + xmlSerializer = new XmlSerializer(typeof(LoginViewModel)); + if (typeof(T) == typeof(Location)) + xmlSerializer = new XmlSerializer(typeof(Location)); + if (typeof(T) == typeof(IndexViewModel)) + xmlSerializer = new XmlSerializer(typeof(IndexViewModel)); + if (typeof(T) == typeof(MyEventsListerViewModel)) + xmlSerializer = new XmlSerializer(typeof(MyEventsListerViewModel)); +#endif + } + + [Benchmark(Description = nameof(XmlSerializer))] + public void XmlSerializer_() + { + memoryStream.Position = 0; + xmlSerializer.Serialize(memoryStream, value); + } + + [Benchmark(Description = nameof(DataContractSerializer))] + public void DataContractSerializer_() + { + memoryStream.Position = 0; + dataContractSerializer.WriteObject(memoryStream, value); + } + + // YAXSerializer is not included in the benchmarks because it does not allow to serialize to stream (only to file and string) + + [GlobalCleanup] + public void Dispose() => memoryStream.Dispose(); + } + + public class SgenConfig : ManualConfig + { + public SgenConfig() + { + Add(Job.Dry.With(RunStrategy.ColdStart).WithLaunchCount(10) // Dry job is 1 execution without pre-Jitting + .WithCustomBuildConfiguration("SGEN") // this is going to use Microsoft.XmlSerializer.Generator + .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)).WithId("SGEN")); + + Add(Job.Dry.With(RunStrategy.ColdStart).WithLaunchCount(10) // Dry job is 1 execution without pre-Jitting + .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)).WithId("NO_SGEN")); + + // to make sure that Benchmarks.XmlSerializers.dll file exists (https://github.com/dotnet/core/blob/master/samples/xmlserializergenerator-instructions.md) + // you can uncomment the line below + KeepBenchmarkFiles = true; + } + } +} From f1a6fecd54ff9c50c5985adb35b441d7f6d22261 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 6 Apr 2018 14:06:32 +0200 Subject: [PATCH 04/10] define the default config + runner --- .gitignore | 1 + src/benchmarks/Runner/Program.cs | 108 ++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8e0bce4abb3..732ca311a56 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ **/.vs/** +**/BenchmarkDotNet.Artifacts/** diff --git a/src/benchmarks/Runner/Program.cs b/src/benchmarks/Runner/Program.cs index 1667ffbbb55..8e12a0bffcc 100644 --- a/src/benchmarks/Runner/Program.cs +++ b/src/benchmarks/Runner/Program.cs @@ -1,4 +1,17 @@ using System; +using System.Linq; +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Exporters.Csv; +using BenchmarkDotNet.Horology; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Toolchains.CsProj; +using BenchmarkDotNet.Toolchains.DotNetCli; +using Definitions.Serializers; namespace Runner { @@ -6,7 +19,100 @@ class Program { static void Main(string[] args) { - Console.WriteLine("Hello World!"); + BenchmarkSwitcher + .FromTypes + ( + GetOpenGenericBenchmarks() + .SelectMany(openGeneric => + GetViewModels().Select(viewModel => openGeneric.MakeGenericType(viewModel))) + .ToArray() + ) + .Run(args, new SimpleConfig()); + } + + static Type[] GetOpenGenericBenchmarks() + => new[] + { + typeof(Json_ToString<>), + typeof(Json_ToStream<>), + typeof(Json_FromString<>), + typeof(Json_FromStream<>), + typeof(Xml_ToStream<>), + typeof(Xml_FromStream<>), + typeof(Binary_ToStream<>), + typeof(Binary_FromStream<>) + }; + + static Type[] GetViewModels() + => new[] + { + typeof(LoginViewModel), + typeof(Location), + typeof(IndexViewModel), + typeof(MyEventsListerViewModel) + }; + } + + public class SimpleConfig : ManualConfig + { + public SimpleConfig() + { + Add(Job.ShortRun); // let's use the Short Run for better first user experience + + Add(MemoryDiagnoser.Default); + + Add(DefaultConfig.Instance.GetValidators().ToArray()); + Add(DefaultConfig.Instance.GetLoggers().ToArray()); + Add(DefaultConfig.Instance.GetColumnProviders().ToArray()); + + Add(new CsvMeasurementsExporter(CsvSeparator.Semicolon)); // you can use Excel to load and merge all the data together + //Add(RPlotExporter.Default); // it produces nice plots but requires R to be installed + Add(MarkdownExporter.GitHub); + Add(HtmlExporter.Default); + //Add(StatisticColumn.AllStatistics); // uncomment it if you want to see more statistics + + Set(new BenchmarkDotNet.Reports.SummaryStyle + { + PrintUnitsInHeader = true, + PrintUnitsInContent = false, + TimeUnit = TimeUnit.Microsecond, + SizeUnit = SizeUnit.B + }); + } + } + + /// + /// this config allows you to run benchmarks for multiple runtimes + /// + public class MultipleRuntimesConfig : ManualConfig + { + public MultipleRuntimesConfig() + { + Add(Job.Default.With(Runtime.Core).With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp20)).AsBaseline().WithId("2.0")); + Add(Job.Default.With(Runtime.Core).With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)).WithId("2.1")); + + Add(Job.Default.With(Runtime.Clr).WithId("Clr")); + Add(Job.Default.With(Runtime.Mono).WithId("Mono")); // you can comment this if you don't have Mono installed + + Add(MemoryDiagnoser.Default); + + Add(DefaultConfig.Instance.GetValidators().ToArray()); + Add(DefaultConfig.Instance.GetLoggers().ToArray()); + Add(DefaultConfig.Instance.GetColumnProviders().ToArray()); + + Add(new CsvMeasurementsExporter(CsvSeparator.Semicolon)); + //Add(RPlotExporter.Default); // it produces nice plots but requires R to be installed + Add(MarkdownExporter.GitHub); + Add(HtmlExporter.Default); + //Add(StatisticColumn.AllStatistics); + + Set(new BenchmarkDotNet.Reports.SummaryStyle + { + PrintUnitsInHeader = true, + PrintUnitsInContent = false, + TimeUnit = TimeUnit.Microsecond, + SizeUnit = SizeUnit.B + }); } } } From b8d25040038cf10b3cd77c8936ac4ba6a5b9aded Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 6 Apr 2018 17:26:36 +0200 Subject: [PATCH 05/10] target .NET 4.6 too, set PlatformTarget in explicit way to allow creating 32/64 bit benchmarks, emit full pdb info to get disassembler support --- src/benchmarks/Definitions/Definitions.csproj | 2 +- src/benchmarks/Runner/Runner.csproj | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/benchmarks/Definitions/Definitions.csproj b/src/benchmarks/Definitions/Definitions.csproj index aeb0417ff32..d0bcd5d697a 100644 --- a/src/benchmarks/Definitions/Definitions.csproj +++ b/src/benchmarks/Definitions/Definitions.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + netstandard2.0;net46 diff --git a/src/benchmarks/Runner/Runner.csproj b/src/benchmarks/Runner/Runner.csproj index 2ac557c541b..b81217eba37 100644 --- a/src/benchmarks/Runner/Runner.csproj +++ b/src/benchmarks/Runner/Runner.csproj @@ -2,7 +2,10 @@ Exe - net461;netcoreapp2.0;netcoreapp2.1 + net46;netcoreapp2.0;netcoreapp2.1 + AnyCPU + pdbonly + true From 6beaac2006ded3ac7b725bc4b4e8db005af1d2a4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 9 Apr 2018 16:27:40 +0200 Subject: [PATCH 06/10] simplify the folder and project structure --- .../Definitions.csproj => Benchmarks.csproj} | 6 +++++- src/benchmarks/Benchmarks.sln | 8 +------- src/benchmarks/{Runner => }/Program.cs | 4 ++-- src/benchmarks/Runner/Runner.csproj | 15 --------------- .../Serializers/Binary_FromStream.cs | 4 ++-- .../Serializers/Binary_ToStream.cs | 4 ++-- .../Serializers/DataGenerator.cs | 3 ++- .../Helpers/DateTimeOffsetSurrogate.cs | 2 +- .../Serializers/Helpers/Workarounds.cs | 0 .../Serializers/Json_FromStream.cs | 2 +- .../Serializers/Json_FromString.cs | 2 +- .../Serializers/Json_ToStream.cs | 2 +- .../Serializers/Json_ToString.cs | 2 +- .../Serializers/Xml_FromStream.cs | 2 +- .../{Definitions => }/Serializers/Xml_ToStream.cs | 2 +- 15 files changed, 21 insertions(+), 37 deletions(-) rename src/benchmarks/{Definitions/Definitions.csproj => Benchmarks.csproj} (79%) rename src/benchmarks/{Runner => }/Program.cs (98%) delete mode 100644 src/benchmarks/Runner/Runner.csproj rename src/benchmarks/{Definitions => }/Serializers/Binary_FromStream.cs (97%) rename src/benchmarks/{Definitions => }/Serializers/Binary_ToStream.cs (95%) rename src/benchmarks/{Definitions => }/Serializers/DataGenerator.cs (99%) rename src/benchmarks/{Definitions => }/Serializers/Helpers/DateTimeOffsetSurrogate.cs (92%) rename src/benchmarks/{Definitions => }/Serializers/Helpers/Workarounds.cs (100%) rename src/benchmarks/{Definitions => }/Serializers/Json_FromStream.cs (99%) rename src/benchmarks/{Definitions => }/Serializers/Json_FromString.cs (96%) rename src/benchmarks/{Definitions => }/Serializers/Json_ToStream.cs (98%) rename src/benchmarks/{Definitions => }/Serializers/Json_ToString.cs (95%) rename src/benchmarks/{Definitions => }/Serializers/Xml_FromStream.cs (98%) rename src/benchmarks/{Definitions => }/Serializers/Xml_ToStream.cs (98%) diff --git a/src/benchmarks/Definitions/Definitions.csproj b/src/benchmarks/Benchmarks.csproj similarity index 79% rename from src/benchmarks/Definitions/Definitions.csproj rename to src/benchmarks/Benchmarks.csproj index d0bcd5d697a..98aeff89a1d 100644 --- a/src/benchmarks/Definitions/Definitions.csproj +++ b/src/benchmarks/Benchmarks.csproj @@ -1,7 +1,11 @@ - netstandard2.0;net46 + Exe + net46;netcoreapp2.0;netcoreapp2.1 + AnyCPU + pdbonly + true diff --git a/src/benchmarks/Benchmarks.sln b/src/benchmarks/Benchmarks.sln index 14cd7f37cbc..52db9697947 100644 --- a/src/benchmarks/Benchmarks.sln +++ b/src/benchmarks/Benchmarks.sln @@ -3,9 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27428.2011 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Runner", "Runner\Runner.csproj", "{D99F63AE-3154-4F13-9424-FA5F9D032D1D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Definitions", "Definitions\Definitions.csproj", "{5DB7E599-9242-4A8F-BA0F-DB691155E733}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks.csproj", "{D99F63AE-3154-4F13-9424-FA5F9D032D1D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -17,10 +15,6 @@ Global {D99F63AE-3154-4F13-9424-FA5F9D032D1D}.Debug|Any CPU.Build.0 = Debug|Any CPU {D99F63AE-3154-4F13-9424-FA5F9D032D1D}.Release|Any CPU.ActiveCfg = Release|Any CPU {D99F63AE-3154-4F13-9424-FA5F9D032D1D}.Release|Any CPU.Build.0 = Release|Any CPU - {5DB7E599-9242-4A8F-BA0F-DB691155E733}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5DB7E599-9242-4A8F-BA0F-DB691155E733}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5DB7E599-9242-4A8F-BA0F-DB691155E733}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5DB7E599-9242-4A8F-BA0F-DB691155E733}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/benchmarks/Runner/Program.cs b/src/benchmarks/Program.cs similarity index 98% rename from src/benchmarks/Runner/Program.cs rename to src/benchmarks/Program.cs index 8e12a0bffcc..e54b1b92cf0 100644 --- a/src/benchmarks/Runner/Program.cs +++ b/src/benchmarks/Program.cs @@ -11,9 +11,9 @@ using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.CsProj; using BenchmarkDotNet.Toolchains.DotNetCli; -using Definitions.Serializers; +using Benchmarks.Serializers; -namespace Runner +namespace Benchmarks { class Program { diff --git a/src/benchmarks/Runner/Runner.csproj b/src/benchmarks/Runner/Runner.csproj deleted file mode 100644 index b81217eba37..00000000000 --- a/src/benchmarks/Runner/Runner.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net46;netcoreapp2.0;netcoreapp2.1 - AnyCPU - pdbonly - true - - - - - - - diff --git a/src/benchmarks/Definitions/Serializers/Binary_FromStream.cs b/src/benchmarks/Serializers/Binary_FromStream.cs similarity index 97% rename from src/benchmarks/Definitions/Serializers/Binary_FromStream.cs rename to src/benchmarks/Serializers/Binary_FromStream.cs index caea09c1bb9..7e024eb7661 100644 --- a/src/benchmarks/Definitions/Serializers/Binary_FromStream.cs +++ b/src/benchmarks/Serializers/Binary_FromStream.cs @@ -2,9 +2,9 @@ using System.IO; using System.Runtime.Serialization.Formatters.Binary; using BenchmarkDotNet.Attributes; -using Definitions.Serializers.Helpers; +using Benchmarks.Serializers.Helpers; -namespace Definitions.Serializers +namespace Benchmarks.Serializers { public class Binary_FromStream where T : IVerifiable { diff --git a/src/benchmarks/Definitions/Serializers/Binary_ToStream.cs b/src/benchmarks/Serializers/Binary_ToStream.cs similarity index 95% rename from src/benchmarks/Definitions/Serializers/Binary_ToStream.cs rename to src/benchmarks/Serializers/Binary_ToStream.cs index 1d9fa94a00b..9ba63a593ea 100644 --- a/src/benchmarks/Definitions/Serializers/Binary_ToStream.cs +++ b/src/benchmarks/Serializers/Binary_ToStream.cs @@ -2,9 +2,9 @@ using System.IO; using System.Runtime.Serialization.Formatters.Binary; using BenchmarkDotNet.Attributes; -using Definitions.Serializers.Helpers; +using Benchmarks.Serializers.Helpers; -namespace Definitions.Serializers +namespace Benchmarks.Serializers { public class Binary_ToStream { diff --git a/src/benchmarks/Definitions/Serializers/DataGenerator.cs b/src/benchmarks/Serializers/DataGenerator.cs similarity index 99% rename from src/benchmarks/Definitions/Serializers/DataGenerator.cs rename to src/benchmarks/Serializers/DataGenerator.cs index 618634b2797..1c9d5d4df9b 100644 --- a/src/benchmarks/Definitions/Serializers/DataGenerator.cs +++ b/src/benchmarks/Serializers/DataGenerator.cs @@ -4,6 +4,7 @@ using MessagePack; using ProtoBuf; using ZeroFormatter; + #if SGEN // the new SGEN tool fails to load some of the dependencies, so we need to replace the problematic dependencies for this particular build configuration // see https://github.com/dotnet/corefx/issues/27281#issuecomment-367449130 fore more @@ -12,7 +13,7 @@ #endif -namespace Definitions.Serializers +namespace Benchmarks.Serializers { internal static class DataGenerator { diff --git a/src/benchmarks/Definitions/Serializers/Helpers/DateTimeOffsetSurrogate.cs b/src/benchmarks/Serializers/Helpers/DateTimeOffsetSurrogate.cs similarity index 92% rename from src/benchmarks/Definitions/Serializers/Helpers/DateTimeOffsetSurrogate.cs rename to src/benchmarks/Serializers/Helpers/DateTimeOffsetSurrogate.cs index 387b8606cd8..fbb1e4196fe 100644 --- a/src/benchmarks/Definitions/Serializers/Helpers/DateTimeOffsetSurrogate.cs +++ b/src/benchmarks/Serializers/Helpers/DateTimeOffsetSurrogate.cs @@ -1,7 +1,7 @@ using System; using ProtoBuf; -namespace Definitions.Serializers.Helpers +namespace Benchmarks.Serializers.Helpers { [ProtoContract] public class DateTimeOffsetSurrogate diff --git a/src/benchmarks/Definitions/Serializers/Helpers/Workarounds.cs b/src/benchmarks/Serializers/Helpers/Workarounds.cs similarity index 100% rename from src/benchmarks/Definitions/Serializers/Helpers/Workarounds.cs rename to src/benchmarks/Serializers/Helpers/Workarounds.cs diff --git a/src/benchmarks/Definitions/Serializers/Json_FromStream.cs b/src/benchmarks/Serializers/Json_FromStream.cs similarity index 99% rename from src/benchmarks/Definitions/Serializers/Json_FromStream.cs rename to src/benchmarks/Serializers/Json_FromStream.cs index d768fdb17be..80e2555e7a0 100644 --- a/src/benchmarks/Definitions/Serializers/Json_FromStream.cs +++ b/src/benchmarks/Serializers/Json_FromStream.cs @@ -3,7 +3,7 @@ using System.Text; using BenchmarkDotNet.Attributes; -namespace Definitions.Serializers +namespace Benchmarks.Serializers { public class Json_FromStream { diff --git a/src/benchmarks/Definitions/Serializers/Json_FromString.cs b/src/benchmarks/Serializers/Json_FromString.cs similarity index 96% rename from src/benchmarks/Definitions/Serializers/Json_FromString.cs rename to src/benchmarks/Serializers/Json_FromString.cs index 8c647bb061b..60866b839b8 100644 --- a/src/benchmarks/Definitions/Serializers/Json_FromString.cs +++ b/src/benchmarks/Serializers/Json_FromString.cs @@ -1,6 +1,6 @@ using BenchmarkDotNet.Attributes; -namespace Definitions.Serializers +namespace Benchmarks.Serializers { public class Json_FromString { diff --git a/src/benchmarks/Definitions/Serializers/Json_ToStream.cs b/src/benchmarks/Serializers/Json_ToStream.cs similarity index 98% rename from src/benchmarks/Definitions/Serializers/Json_ToStream.cs rename to src/benchmarks/Serializers/Json_ToStream.cs index 6ec822f19e7..05113694f98 100644 --- a/src/benchmarks/Definitions/Serializers/Json_ToStream.cs +++ b/src/benchmarks/Serializers/Json_ToStream.cs @@ -3,7 +3,7 @@ using System.Text; using BenchmarkDotNet.Attributes; -namespace Definitions.Serializers +namespace Benchmarks.Serializers { public class Json_ToStream { diff --git a/src/benchmarks/Definitions/Serializers/Json_ToString.cs b/src/benchmarks/Serializers/Json_ToString.cs similarity index 95% rename from src/benchmarks/Definitions/Serializers/Json_ToString.cs rename to src/benchmarks/Serializers/Json_ToString.cs index a0846bc2c6f..8e64b197d2a 100644 --- a/src/benchmarks/Definitions/Serializers/Json_ToString.cs +++ b/src/benchmarks/Serializers/Json_ToString.cs @@ -1,6 +1,6 @@ using BenchmarkDotNet.Attributes; -namespace Definitions.Serializers +namespace Benchmarks.Serializers { public class Json_ToString { diff --git a/src/benchmarks/Definitions/Serializers/Xml_FromStream.cs b/src/benchmarks/Serializers/Xml_FromStream.cs similarity index 98% rename from src/benchmarks/Definitions/Serializers/Xml_FromStream.cs rename to src/benchmarks/Serializers/Xml_FromStream.cs index 6de8ea95fb2..2060eeff53c 100644 --- a/src/benchmarks/Definitions/Serializers/Xml_FromStream.cs +++ b/src/benchmarks/Serializers/Xml_FromStream.cs @@ -3,7 +3,7 @@ using System.Xml.Serialization; using BenchmarkDotNet.Attributes; -namespace Definitions.Serializers +namespace Benchmarks.Serializers { public class Xml_FromStream { diff --git a/src/benchmarks/Definitions/Serializers/Xml_ToStream.cs b/src/benchmarks/Serializers/Xml_ToStream.cs similarity index 98% rename from src/benchmarks/Definitions/Serializers/Xml_ToStream.cs rename to src/benchmarks/Serializers/Xml_ToStream.cs index 087da8bf206..6a5221f9688 100644 --- a/src/benchmarks/Definitions/Serializers/Xml_ToStream.cs +++ b/src/benchmarks/Serializers/Xml_ToStream.cs @@ -8,7 +8,7 @@ using BenchmarkDotNet.Toolchains.CsProj; using BenchmarkDotNet.Toolchains.DotNetCli; -namespace Definitions.Serializers +namespace Benchmarks.Serializers { //[Config(typeof(SgenConfig))] // currently blocked https://github.com/dotnet/corefx/issues/27281#issuecomment-367533728 public class Xml_ToStream From bd2224876da34a9d5e33d537cbde320c65a50236 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 9 Apr 2018 16:36:37 +0200 Subject: [PATCH 07/10] remove the SGEN workarounds --- src/benchmarks/Serializers/DataGenerator.cs | 8 --- .../Serializers/Helpers/Workarounds.cs | 49 ------------------- src/benchmarks/Serializers/Xml_ToStream.cs | 35 ------------- 3 files changed, 92 deletions(-) delete mode 100644 src/benchmarks/Serializers/Helpers/Workarounds.cs diff --git a/src/benchmarks/Serializers/DataGenerator.cs b/src/benchmarks/Serializers/DataGenerator.cs index 1c9d5d4df9b..4b9c834856d 100644 --- a/src/benchmarks/Serializers/DataGenerator.cs +++ b/src/benchmarks/Serializers/DataGenerator.cs @@ -5,14 +5,6 @@ using ProtoBuf; using ZeroFormatter; -#if SGEN -// the new SGEN tool fails to load some of the dependencies, so we need to replace the problematic dependencies for this particular build configuration -// see https://github.com/dotnet/corefx/issues/27281#issuecomment-367449130 fore more -using Benchmarks.Serializers.Helpers; -#else - -#endif - namespace Benchmarks.Serializers { internal static class DataGenerator diff --git a/src/benchmarks/Serializers/Helpers/Workarounds.cs b/src/benchmarks/Serializers/Helpers/Workarounds.cs deleted file mode 100644 index cde45d2ebdd..00000000000 --- a/src/benchmarks/Serializers/Helpers/Workarounds.cs +++ /dev/null @@ -1,49 +0,0 @@ -#if SGEN -// the new SGEN tool fails to load some of the dependencies, so we need to replace the problematic dependencies for this particular build configuration -using System; -using System.Collections.Generic; -using System.Text; - -namespace Benchmarks.Serializers.Helpers -{ - public class ProtoContractAttribute : Attribute - { - public ProtoContractAttribute() { } - } - - public class ProtoMemberAttribute : Attribute - { - public ProtoMemberAttribute(int tag) { } - } - - public class ZeroFormattableAttribute : Attribute - { - public ZeroFormattableAttribute() { } - } - - public class MessagePackObjectAttribute : Attribute - { - public MessagePackObjectAttribute() { } - } - - public class IndexAttribute : Attribute - { - public IndexAttribute(int index) { } - } - - public class KeyAttribute : Attribute - { - public KeyAttribute(int key) { } - } - - public class IgnoreFormatAttribute : Attribute - { - public IgnoreFormatAttribute() { } - } - - public class IgnoreMemberAttribute : Attribute - { - public IgnoreMemberAttribute() { } - } -} -#endif \ No newline at end of file diff --git a/src/benchmarks/Serializers/Xml_ToStream.cs b/src/benchmarks/Serializers/Xml_ToStream.cs index 6a5221f9688..2aa80876a30 100644 --- a/src/benchmarks/Serializers/Xml_ToStream.cs +++ b/src/benchmarks/Serializers/Xml_ToStream.cs @@ -2,15 +2,9 @@ using System.Runtime.Serialization; using System.Xml.Serialization; using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Toolchains.CsProj; -using BenchmarkDotNet.Toolchains.DotNetCli; namespace Benchmarks.Serializers { - //[Config(typeof(SgenConfig))] // currently blocked https://github.com/dotnet/corefx/issues/27281#issuecomment-367533728 public class Xml_ToStream { private readonly T value; @@ -24,18 +18,6 @@ public Xml_ToStream() xmlSerializer = new XmlSerializer(typeof(T)); dataContractSerializer = new DataContractSerializer(typeof(T)); memoryStream = new MemoryStream(capacity: short.MaxValue); - -#if SGEN - // we need to give some hints to the SGEN tool ;) - if (typeof(T) == typeof(LoginViewModel)) - xmlSerializer = new XmlSerializer(typeof(LoginViewModel)); - if (typeof(T) == typeof(Location)) - xmlSerializer = new XmlSerializer(typeof(Location)); - if (typeof(T) == typeof(IndexViewModel)) - xmlSerializer = new XmlSerializer(typeof(IndexViewModel)); - if (typeof(T) == typeof(MyEventsListerViewModel)) - xmlSerializer = new XmlSerializer(typeof(MyEventsListerViewModel)); -#endif } [Benchmark(Description = nameof(XmlSerializer))] @@ -57,21 +39,4 @@ public void DataContractSerializer_() [GlobalCleanup] public void Dispose() => memoryStream.Dispose(); } - - public class SgenConfig : ManualConfig - { - public SgenConfig() - { - Add(Job.Dry.With(RunStrategy.ColdStart).WithLaunchCount(10) // Dry job is 1 execution without pre-Jitting - .WithCustomBuildConfiguration("SGEN") // this is going to use Microsoft.XmlSerializer.Generator - .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)).WithId("SGEN")); - - Add(Job.Dry.With(RunStrategy.ColdStart).WithLaunchCount(10) // Dry job is 1 execution without pre-Jitting - .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)).WithId("NO_SGEN")); - - // to make sure that Benchmarks.XmlSerializers.dll file exists (https://github.com/dotnet/core/blob/master/samples/xmlserializergenerator-instructions.md) - // you can uncomment the line below - KeepBenchmarkFiles = true; - } - } } From c02502d4d6eff35f468f17aaa1d11bd06a977cb7 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 16 Apr 2018 16:18:10 +0200 Subject: [PATCH 08/10] support command line arugments for customizing the config --- src/benchmarks/Benchmarks.csproj | 3 +- src/benchmarks/Program.cs | 188 +++++++++++++----- .../Serializers/SerializerBenchmarks.cs | 35 ++++ 3 files changed, 174 insertions(+), 52 deletions(-) create mode 100644 src/benchmarks/Serializers/SerializerBenchmarks.cs diff --git a/src/benchmarks/Benchmarks.csproj b/src/benchmarks/Benchmarks.csproj index 98aeff89a1d..74b4b015c52 100644 --- a/src/benchmarks/Benchmarks.csproj +++ b/src/benchmarks/Benchmarks.csproj @@ -9,7 +9,8 @@ - + + diff --git a/src/benchmarks/Program.cs b/src/benchmarks/Program.cs index e54b1b92cf0..1d47e20bc96 100644 --- a/src/benchmarks/Program.cs +++ b/src/benchmarks/Program.cs @@ -1,4 +1,4 @@ -using System; +using System.Collections.Generic; using System.Linq; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; @@ -9,76 +9,161 @@ using BenchmarkDotNet.Horology; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; +using BenchmarkDotNet.Toolchains.CoreRt; using BenchmarkDotNet.Toolchains.CsProj; +using BenchmarkDotNet.Toolchains.CustomCoreClr; using BenchmarkDotNet.Toolchains.DotNetCli; +using BenchmarkDotNet.Toolchains.InProcess; using Benchmarks.Serializers; +using CommandLine; namespace Benchmarks { class Program { static void Main(string[] args) - { - BenchmarkSwitcher + => Parser.Default.ParseArguments(args) + .WithParsed(RunOptionsAndReturnExitCode) + .WithNotParsed(errors => { }); // ignore the errors, the parser prints nice error message + + private static void RunOptionsAndReturnExitCode(Options options) + => BenchmarkSwitcher .FromTypes ( - GetOpenGenericBenchmarks() - .SelectMany(openGeneric => - GetViewModels().Select(viewModel => openGeneric.MakeGenericType(viewModel))) - .ToArray() + SerializerBenchmarks.GetTypes() ) - .Run(args, new SimpleConfig()); + .Run(config: GetConfig(options)); + + private static IConfig GetConfig(Options options) + { + var baseJob = Job.ShortRun; // let's use the Short Run for better first user experience ;) + var jobs = GetJobs(options, baseJob).ToArray(); + + var config = DefaultConfig.Instance + .With(jobs.Any() ? jobs : new[] { baseJob }); + + if (options.UseMemoryDiagnoser) + config = config.With(MemoryDiagnoser.Default); + if (options.UseDisassemblyDiagnoser) + config = config.With(DisassemblyDiagnoser.Create(DisassemblyDiagnoserConfig.Asm)); + + if (options.DisplayAllStatistics) + config = config.With(StatisticColumn.AllStatistics); + + return config; } - static Type[] GetOpenGenericBenchmarks() - => new[] - { - typeof(Json_ToString<>), - typeof(Json_ToStream<>), - typeof(Json_FromString<>), - typeof(Json_FromStream<>), - typeof(Xml_ToStream<>), - typeof(Xml_FromStream<>), - typeof(Binary_ToStream<>), - typeof(Binary_FromStream<>) - }; - - static Type[] GetViewModels() - => new[] + private static IEnumerable GetJobs(Options options, Job baseJob) + { + if (options.RunInProcess) + yield return baseJob.With(InProcessToolchain.Instance); + + if (options.RunClr) + yield return baseJob.With(Runtime.Clr); + if (!string.IsNullOrEmpty(options.ClrVersion)) + yield return baseJob.With(new ClrRuntime(options.ClrVersion)); + + if (options.RunMono) + yield return baseJob.With(Runtime.Mono); + if (!string.IsNullOrEmpty(options.MonoPath)) + yield return baseJob.With(new MonoRuntime("Mono", options.MonoPath)); + + if (options.RunCoreRt) + yield return baseJob.With(Runtime.CoreRT).With(CoreRtToolchain.LatestMyGetBuild); + if (!string.IsNullOrEmpty(options.CoreRtVersion)) + yield return baseJob.With(Runtime.CoreRT).With(CoreRtToolchain.CreateBuilder().UseCoreRtNuGet(options.CoreRtVersion).ToToolchain()); + + if (options.RunCore) + yield return baseJob.With(Runtime.Core).With(CsProjCoreToolchain.Current.Value); + if (options.RunCore20) + yield return baseJob.With(Runtime.Core).With(CsProjCoreToolchain.NetCoreApp20); + if (options.RunCore21) + yield return baseJob.With(Runtime.Core).With(CsProjCoreToolchain.NetCoreApp21); + + if (!string.IsNullOrEmpty(options.CoreFxVersion) || !string.IsNullOrEmpty(options.CoreClrVersion)) { - typeof(LoginViewModel), - typeof(Location), - typeof(IndexViewModel), - typeof(MyEventsListerViewModel) - }; + var builder = CustomCoreClrToolchain.CreateBuilder(); + + if (!string.IsNullOrEmpty(options.CoreFxVersion) && !string.IsNullOrEmpty(options.CoreFxBinPackagesPath)) + builder.UseCoreFxLocalBuild(options.CoreFxVersion, options.CoreFxBinPackagesPath); + else if (!string.IsNullOrEmpty(options.CoreFxVersion)) + builder.UseCoreFxNuGet(options.CoreFxVersion); + else + builder.UseCoreFxDefault(); + + if (!string.IsNullOrEmpty(options.CoreClrVersion) && !string.IsNullOrEmpty(options.CoreClrBinPackagesPath) && !string.IsNullOrEmpty(options.CoreClrPackagesPath)) + builder.UseCoreClrLocalBuild(options.CoreClrVersion, options.CoreClrBinPackagesPath, options.CoreClrPackagesPath); + else if (!string.IsNullOrEmpty(options.CoreClrVersion)) + builder.UseCoreClrNuGet(options.CoreClrVersion); + else + builder.UseCoreClrDefault(); + + if (!string.IsNullOrEmpty(options.CliPath)) + builder.DotNetCli(options.CliPath); + + yield return baseJob.With(Runtime.Core).With(builder.ToToolchain()); + } + } } - public class SimpleConfig : ManualConfig + public class Options { - public SimpleConfig() - { - Add(Job.ShortRun); // let's use the Short Run for better first user experience + [Option("memory", Required = false, Default = true, HelpText = "Prints memory statistics. Enabled by default")] + public bool UseMemoryDiagnoser { get; set; } - Add(MemoryDiagnoser.Default); + [Option("disassm", Required = false, Default = false, HelpText = "Gets diassembly for benchmarked code")] + public bool UseDisassemblyDiagnoser { get; set; } - Add(DefaultConfig.Instance.GetValidators().ToArray()); - Add(DefaultConfig.Instance.GetLoggers().ToArray()); - Add(DefaultConfig.Instance.GetColumnProviders().ToArray()); + [Option("allStats", Required = false, Default = false, HelpText = "Displays all statistics (min, max & more")] + public bool DisplayAllStatistics { get; set; } - Add(new CsvMeasurementsExporter(CsvSeparator.Semicolon)); // you can use Excel to load and merge all the data together - //Add(RPlotExporter.Default); // it produces nice plots but requires R to be installed - Add(MarkdownExporter.GitHub); - Add(HtmlExporter.Default); - //Add(StatisticColumn.AllStatistics); // uncomment it if you want to see more statistics + [Option("inProcess", Required = false, Default = false, HelpText = "Run benchmarks in Process")] + public bool RunInProcess { get; set; } - Set(new BenchmarkDotNet.Reports.SummaryStyle - { - PrintUnitsInHeader = true, - PrintUnitsInContent = false, - TimeUnit = TimeUnit.Microsecond, - SizeUnit = SizeUnit.B - }); - } + [Option("clr", Required = false, Default = false, HelpText = "Run benchmarks for Clr")] + public bool RunClr { get; set; } + + [Option("clrVersion", Required = false, HelpText = "Optional version of private CLR build used as the value of COMPLUS_Version env var.")] + public string ClrVersion { get; set; } + + [Option("mono", Required = false, Default = false, HelpText = "Run benchmarks for Mono (takes the default from PATH)")] + public bool RunMono { get; set; } + + [Option("monoPath", Required = false, HelpText = "Optional path to Mono which should be used for running benchmarks.")] + public string MonoPath { get; set; } + + [Option("coreRt", Required = false, Default = false, HelpText = "Run benchmarks for the latest CoreRT")] + public bool RunCoreRt { get; set; } + + [Option("coreRtVersion", Required = false, HelpText = "Optional version of Microsoft.DotNet.ILCompiler which should be used to run with CoreRT. Example: \"1.0.0-alpha-26414-01\"")] + public string CoreRtVersion { get; set; } + + [Option("core", Required = false, Default = false, HelpText = "Run benchmarks for .NET Core")] + public bool RunCore { get; set; } + + [Option("core20", Required = false, Default = false, HelpText = "Run benchmarks for .NET Core 2.0")] + public bool RunCore20 { get; set; } + + [Option("core21", Required = false, Default = false, HelpText = "Run benchmarks for .NET Core 2.1")] + public bool RunCore21 { get; set; } + + [Option("cli", Required = false, HelpText = "Optional path to dotnet cli which should be used for running benchmarks.")] + public string CliPath { get; set; } + + [Option("coreClrVersion", Required = false, HelpText = "Optional version of Microsoft.NETCore.Runtime which should be used. Example: \"2.1.0-preview2-26305-0\"")] + public string CoreClrVersion { get; set; } + + [Option("coreClrBin", Required = false, HelpText = @"Optional path to folder with CoreClr NuGet packages. Example: ""C:\coreclr\bin\Product\Windows_NT.x64.Release\.nuget\pkg""")] + public string CoreClrBinPackagesPath { get; set; } + + [Option("coreClrPackages", Required = false, HelpText = @"Optional path to folder with NuGet packages restored for CoreClr build. Example: ""C:\Projects\coreclr\packages""")] + public string CoreClrPackagesPath { get; set; } + + [Option("coreFxVersion", Required = false, HelpText = "Optional version of Microsoft.Private.CoreFx.NETCoreApp which should be used. Example: \"4.5.0-preview2-26307-0\"")] + public string CoreFxVersion { get; set; } + + [Option("coreFxBin", Required = false, HelpText = @"Optional path to folder with CoreFX NuGet packages, Example: ""C:\Projects\forks\corefx\bin\packages\Release""")] + public string CoreFxBinPackagesPath { get; set; } } /// @@ -88,11 +173,12 @@ public class MultipleRuntimesConfig : ManualConfig { public MultipleRuntimesConfig() { - Add(Job.Default.With(Runtime.Core).With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp20)).AsBaseline().WithId("2.0")); - Add(Job.Default.With(Runtime.Core).With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)).WithId("2.1")); + Add(Job.Default.With(Runtime.Core).With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp20)).AsBaseline().WithId("Core 2.0")); + Add(Job.Default.With(Runtime.Core).With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)).WithId("Core 2.1")); Add(Job.Default.With(Runtime.Clr).WithId("Clr")); Add(Job.Default.With(Runtime.Mono).WithId("Mono")); // you can comment this if you don't have Mono installed + Add(Job.Default.With(Runtime.CoreRT).WithId("CoreRT")); Add(MemoryDiagnoser.Default); diff --git a/src/benchmarks/Serializers/SerializerBenchmarks.cs b/src/benchmarks/Serializers/SerializerBenchmarks.cs new file mode 100644 index 00000000000..225add3e09b --- /dev/null +++ b/src/benchmarks/Serializers/SerializerBenchmarks.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; + +namespace Benchmarks.Serializers +{ + internal static class SerializerBenchmarks + { + internal static Type[] GetTypes() + => GetOpenGenericBenchmarks() + .SelectMany(openGeneric => GetViewModels().Select(viewModel => openGeneric.MakeGenericType(viewModel))) + .ToArray(); + + private static Type[] GetOpenGenericBenchmarks() + => new[] + { + typeof(Json_ToString<>), + typeof(Json_ToStream<>), + typeof(Json_FromString<>), + typeof(Json_FromStream<>), + typeof(Xml_ToStream<>), + typeof(Xml_FromStream<>), + typeof(Binary_ToStream<>), + typeof(Binary_FromStream<>) + }; + + private static Type[] GetViewModels() + => new[] + { + typeof(LoginViewModel), + typeof(Location), + typeof(IndexViewModel), + typeof(MyEventsListerViewModel) + }; + } +} \ No newline at end of file From d222af11dd6ce96f48fe0aab9be38ed9c2120ddb Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 17 Apr 2018 13:56:29 +0200 Subject: [PATCH 09/10] the docs --- src/benchmarks/Benchmarks.csproj | 9 +- src/benchmarks/Program.cs | 27 +++- src/benchmarks/README.md | 204 +++++++++++++++++++++++++ src/benchmarks/Serializers/README.md | 34 +++++ src/benchmarks/img/chooseBenchmark.png | Bin 0 -> 105693 bytes src/benchmarks/img/exportedResults.png | Bin 0 -> 22309 bytes src/benchmarks/img/sampleDisassm.png | Bin 0 -> 23337 bytes 7 files changed, 266 insertions(+), 8 deletions(-) create mode 100644 src/benchmarks/README.md create mode 100644 src/benchmarks/Serializers/README.md create mode 100644 src/benchmarks/img/chooseBenchmark.png create mode 100644 src/benchmarks/img/exportedResults.png create mode 100644 src/benchmarks/img/sampleDisassm.png diff --git a/src/benchmarks/Benchmarks.csproj b/src/benchmarks/Benchmarks.csproj index 74b4b015c52..b53ad1a8ed2 100644 --- a/src/benchmarks/Benchmarks.csproj +++ b/src/benchmarks/Benchmarks.csproj @@ -6,10 +6,17 @@ AnyCPU pdbonly true + true - + + + + + + + diff --git a/src/benchmarks/Program.cs b/src/benchmarks/Program.cs index 1d47e20bc96..4ce997e15ac 100644 --- a/src/benchmarks/Program.cs +++ b/src/benchmarks/Program.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; @@ -23,15 +24,12 @@ class Program { static void Main(string[] args) => Parser.Default.ParseArguments(args) - .WithParsed(RunOptionsAndReturnExitCode) + .WithParsed(RunBenchmarks) .WithNotParsed(errors => { }); // ignore the errors, the parser prints nice error message - private static void RunOptionsAndReturnExitCode(Options options) + private static void RunBenchmarks(Options options) => BenchmarkSwitcher - .FromTypes - ( - SerializerBenchmarks.GetTypes() - ) + .FromAssemblyAndTypes(typeof(Program).Assembly, SerializerBenchmarks.GetTypes()) .Run(config: GetConfig(options)); private static IConfig GetConfig(Options options) @@ -71,7 +69,17 @@ private static IEnumerable GetJobs(Options options, Job baseJob) if (options.RunCoreRt) yield return baseJob.With(Runtime.CoreRT).With(CoreRtToolchain.LatestMyGetBuild); if (!string.IsNullOrEmpty(options.CoreRtVersion)) - yield return baseJob.With(Runtime.CoreRT).With(CoreRtToolchain.CreateBuilder().UseCoreRtNuGet(options.CoreRtVersion).ToToolchain()); + yield return baseJob.With(Runtime.CoreRT) + .With(CoreRtToolchain.CreateBuilder() + .UseCoreRtNuGet(options.CoreRtVersion) + .AdditionalNuGetFeed("benchmarkdotnet ci", "https://ci.appveyor.com/nuget/benchmarkdotnet") + .ToToolchain()); + if (!string.IsNullOrEmpty(options.CoreRtPath)) + yield return baseJob.With(Runtime.CoreRT) + .With(CoreRtToolchain.CreateBuilder() + .UseCoreRtLocal(options.CoreRtPath) + .AdditionalNuGetFeed("benchmarkdotnet ci", "https://ci.appveyor.com/nuget/benchmarkdotnet") + .ToToolchain()); if (options.RunCore) yield return baseJob.With(Runtime.Core).With(CsProjCoreToolchain.Current.Value); @@ -101,6 +109,8 @@ private static IEnumerable GetJobs(Options options, Job baseJob) if (!string.IsNullOrEmpty(options.CliPath)) builder.DotNetCli(options.CliPath); + builder.AdditionalNuGetFeed("benchmarkdotnet ci", "https://ci.appveyor.com/nuget/benchmarkdotnet"); + yield return baseJob.With(Runtime.Core).With(builder.ToToolchain()); } } @@ -138,6 +148,9 @@ public class Options [Option("coreRtVersion", Required = false, HelpText = "Optional version of Microsoft.DotNet.ILCompiler which should be used to run with CoreRT. Example: \"1.0.0-alpha-26414-01\"")] public string CoreRtVersion { get; set; } + [Option("ilcPath", Required = false, HelpText = "Optional IlcPath which should be used to run with private CoreRT build. Example: \"1.0.0-alpha-26414-01\"")] + public string CoreRtPath { get; set; } + [Option("core", Required = false, Default = false, HelpText = "Run benchmarks for .NET Core")] public bool RunCore { get; set; } diff --git a/src/benchmarks/README.md b/src/benchmarks/README.md new file mode 100644 index 00000000000..2b560a3532f --- /dev/null +++ b/src/benchmarks/README.md @@ -0,0 +1,204 @@ +# Benchmarks + +This repo contains various .NET benchmarks. It uses BenchmarkDotNet as the benchmarking engine to run benchmarks for .NET, .NET Core, CoreRT and Mono. Including private runtime builds. + +## BenchmarkDotNet + +Benchmarking is really hard (especially microbenchmarking), you can easily make a mistake during performance measurements. +BenchmarkDotNet will protect you from the common pitfalls (even for experienced developers) because it does all the dirty work for you: + +* it generates an isolated project per runtime +* it builds the project in `Release` +* it runs every benchmark in a stand-alone process (to achieve process isolation and avoid side effects) +* it estimates the perfect invocation count per iteration +* it warms-up the code +* it evaluates the overhead +* it runs multiple iterations of the method until the requested level of precision is met. + +A few useful links for you: + +* If you want to know more about BenchmarkDotNet features, check out the [Overview Page](http://benchmarkdotnet.org/Overview.htm). +* If you want to use BenchmarkDotNet for the first time, the [Getting Started](http://benchmarkdotnet.org/GettingStarted.htm) will help you. +* If you want to ask a quick question or discuss performance topics, use the [gitter](https://gitter.im/dotnet/BenchmarkDotNet) channel. + +## Your first benchmark + +It's really easy to design a performance experiment with BenchmarkDotNet. Just mark your method with the `[Benchmark]` attribute and the benchmark is ready. + +```cs +public class Simple +{ + [Benchmark] + public byte[] CreateByteArray() => new byte[8]; +} +``` + +Any public, non-generic type with public `[Benchmark]` method in this assembly will be auto-detected and added to the benchmarks list. + +## Running + +To run the benchmarks you have to execute `dotnet run -c Release -f net46|netcoreapp2.0|netcoreapp2.1` (choose one of the supported frameworks). + +![Choose Benchmark](./img/chooseBenchmark.png) + +And select one of the benchmarks from the list by either entering it's number or name. To **run all** the benchmarks simply enter `*` to the console. + +BenchmarkDotNet will build the executables, run the benchmarks, print the results to console and **export the results** to `.\BenchmarkDotNet.Artifacts\results`. + +![Exported results](./img/exportedResults.png) + +BenchmarkDotNet by default exports the results to GitHub markdown, so you can just find the right `.md` file in `results` folder and copy-paste the markdown to GitHub. + +## All Statistics + +By default BenchmarkDotNet displays only `Mean`, `Error` and `StdDev` in the results. If you want to see more statistics, please pass `--allStats` as an extra argument to the app: `dotnet run -c Release -f netcoreapp2.1 -- --allStats`. If you build your own config, please use `config.With(StatisticColumn.AllStatistics)`. + +| Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Gen 0 | Allocated | +|--------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|------------:|-------:|----------:| +| Jil | 458.2 ns | 38.63 ns | 2.183 ns | 1.260 ns | 455.8 ns | 455.8 ns | 458.9 ns | 460.0 ns | 460.0 ns | 2,182,387.2 | 0.1163 | 736 B | +| JSON.NET | 869.8 ns | 47.37 ns | 2.677 ns | 1.545 ns | 867.7 ns | 867.7 ns | 868.8 ns | 872.8 ns | 872.8 ns | 1,149,736.0 | 0.2394 | 1512 B | +| Utf8Json | 272.6 ns | 341.64 ns | 19.303 ns | 11.145 ns | 256.7 ns | 256.7 ns | 266.9 ns | 294.1 ns | 294.1 ns | 3,668,854.8 | 0.0300 | 192 B | + +## How to read the Memory Statistics + +The project is configured to include managed memory statistics by using [Memory Diagnoser](http://adamsitnik.com/the-new-Memory-Diagnoser/) + +| Method | Gen 0 | Allocated | +|----------- |------- |---------- | +| A | - | 0 B | +| B | 1 | 496 B | + +* Allocated contains the size of allocated **managed** memory. **Stackalloc/native heap allocations are not included.** It's per single invocation, **inclusive**. +* The `Gen X` column contains the number of `Gen X` collections per ***1 000*** Operations. If the value is equal 1, then it means that GC collects memory once per one thousand of benchmark invocations in generation `X`. BenchmarkDotNet is using some heuristic when running benchmarks, so the number of invocations can be different for different runs. Scaling makes the results comparable. +* `-` in the Gen column means that no garbage collection was performed. +* If `Gen X` column is not present, then it means that no garbage collection was performed for generation `X`. If none of your benchmarks induces the GC, the Gen columns are not present. + +## How to get the Disassembly + +If you want to disassemble the benchmarked code, you need to use the [Disassembly Diagnoser](http://adamsitnik.com/Disassembly-Diagnoser/). It allows to disassemble `asm/C#/IL` in recursive way on Windows for .NET and .NET Core (all Jits) and `asm` for Mono on any OS. + +You can do that by passing `--disassm` to the app or by using `[DisassemblyDiagnoser(printAsm: true, printSource: true)]` attribute or by adding it to your config with `config.With(DisassemblyDiagnoser.Create(new DisassemblyDiagnoserConfig(printAsm: true, recursiveDepth: 1))`. + +![Sample Disassm](./img/sampleDisassm.png) + +## How to run In Process + +If you want to run the benchmarks in process, without creating a dedicated executable and process-level isolation, please pass `--inProcess` as an extra argument to the app: `dotnet run -c Release -f netcoreapp2.1 -- --inProcess`. If you build your own config, please use `config.With(Job.Default.With(InProcessToolchain.Instance))`. Please use this option only when you are sure that the benchmarks you want to run have no side effects. + +## How to compare different Runtimes + +BenchmarkDotNet allows you to run benchmarks for multiple runtimes. By using this feature you can compare .NET vs .NET Core vs CoreRT vs Mono or .NET Core 2.0 vs .NET Core 2.1. BDN will compile and run the right stuff for you. + +* for .NET pass `--clr` to the app or use `Job.Default.With(Runtime.Clr)` in the code. +* for .NET Core 2.0 pass `--core20` to the app or use `Job.Default.With(Runtime.Core).With(CsProjCoreToolchain.NetCoreApp20)` in the code. +* for .NET Core 2.1 pass `--core21` to the app or use `Job.Default.With(Runtime.Core).With(CsProjCoreToolchain.NetCoreApp20)` in the code. +* for the latest CoreRT pass `--coreRt` to the app or use `Job.Default.With(Runtime.CoreRT).With(CoreRtToolchain.LatestMyGetBuild)` in the code. **Be warned!** Downloading latest CoreRT with all the dependencies takes a lot of time. It is recommended to choose one version and use it for comparisions, more info [here](https://github.com/dotnet/BenchmarkDotNet/blob/600e5fa81bd8e7a1d32a60b2bea830e1f46106eb/docs/guide/Configs/Toolchains.md#corert). To use explicit CoreRT version please use `coreRtVersion` argument. Example: `dotnet run -c Release -f netcoreapp2.1 --coreRtVersion 1.0.0-alpha-26414-0` +* for Mono pass `--mono` to the app or use `Job.Default.With(Runtime.Mono)` in the code. + +An example command for comparing 4 runtimes: `dotnet run -c Release -f netcoreapp2.1 -- --core20 --core21 --mono --clr --coreRt` + +``` ini +BenchmarkDotNet=v0.10.14.516-nightly, OS=Windows 10.0.16299.309 (1709/FallCreatorsUpdate/Redstone3) +Intel Xeon CPU E5-1650 v4 3.60GHz, 1 CPU, 12 logical and 6 physical cores +Frequency=3507504 Hz, Resolution=285.1030 ns, Timer=TSC +.NET Core SDK=2.1.300-preview1-008174 + [Host] : .NET Core 2.1.0-preview1-26216-03 (CoreCLR 4.6.26216.04, CoreFX 4.6.26216.02), 64bit RyuJIT + Job-GALXOG : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2633.0 + Job-DRRTOZ : .NET Core 2.0.6 (CoreCLR 4.6.26212.01, CoreFX 4.6.26212.01), 64bit RyuJIT + Job-QQFGIW : .NET Core 2.1.0-preview1-26216-03 (CoreCLR 4.6.26216.04, CoreFX 4.6.26216.02), 64bit RyuJIT + Job-GKRDGF : .NET CoreRT 1.0.26412.02, 64bit AOT + Job-HNFRHF : Mono 5.10.0 (Visual Studio), 64bit + +LaunchCount=1 TargetCount=3 WarmupCount=3 +``` + +| Method | Runtime | Toolchain | Mean | Error | StdDev | Allocated | +|--------- |-------- |----------------------------- |----------:|-----------:|----------:|----------:| +| ParseInt | Clr | Default | 95.95 ns | 5.354 ns | 0.3025 ns | 0 B | +| ParseInt | Core | .NET Core 2.0 | 104.71 ns | 121.620 ns | 6.8718 ns | 0 B | +| ParseInt | Core | .NET Core 2.1 | 93.16 ns | 6.383 ns | 0.3606 ns | 0 B | +| ParseInt | CoreRT | Core RT 1.0.0-alpha-26412-02 | 110.02 ns | 71.947 ns | 4.0651 ns | 0 B | +| ParseInt | Mono | Default | 133.19 ns | 133.928 ns | 7.5672 ns | N/A | + +## .NET Core 2.0 vs .NET Core 2.1 + +If you want to compare .NET Core 2.0 vs .NET Core 2.1 you can just pass `-- --core20 --core21`. You can also build a custom config and mark selected runtime as baseline, then all the results will be scaled to the baseline. + +```cs +Add(Job.Default.With(Runtime.Core).With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp20)).WithId("Core 2.0").AsBaseline()); +Add(Job.Default.With(Runtime.Core).With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)).WithId("Core 2.1")); +``` + +| Method | Job | Toolchain | IsBaseline | Mean | Error | StdDev | Scaled | +|---------------------- |--------- |-------------- |----------- |---------:|----------:|----------:|-------:| +| CompactLoopBodyLayout | Core 2.0 | .NET Core 2.0 | True | 36.72 ns | 0.1583 ns | 0.1481 ns | 1.00 | +| CompactLoopBodyLayout | Core 2.1 | .NET Core 2.1 | Default | 30.47 ns | 0.1731 ns | 0.1619 ns | 0.83 | + +## Benchmarking private CLR build + +It's possible to benchmark a private build of .NET Runtime. You just need to pass the value of `COMPLUS_Version` to BenchmarkDotNet. You can do that by either using `--clrVersion $theVersion` as an arugment or `Job.ShortRun.With(new ClrRuntime(version: "$theVersiong"))` in the code. + +So if you made a change in CLR and want to measure the difference, you can run the benchmarks with `dotnet run -c Release -f net46 --clr --clrVersion $theVersion`. More info can be found [here](https://github.com/dotnet/BenchmarkDotNet/issues/706). + +## Any CoreCLR and CoreFX + +BenchmarkDotNet allows the users to run their benchmarks against ANY CoreCLR and CoreFX builds. You can compare your local build vs MyGet feed or Debug vs Release or one version vs another. + +To avoid problems described [here](https://github.com/dotnet/coreclr/blob/master/Documentation/workflow/UsingDotNetCli.md#update-coreclr-using-runtime-nuget-package) a temporary folder is used when restoring packages for local builds. This is why it takes 20-30s in total to build the benchmarks. + +Entire feature with many examples is described [here](https://github.com/dotnet/BenchmarkDotNet/blob/600e5fa81bd8e7a1d32a60b2bea830e1f46106eb/docs/guide/Configs/Toolchains.md#custom-coreclr-and-corefx). + +### Benchmarking private CoreFX build + +To run benchmarks with private CoreFX build you need to provide the version of `Microsoft.Private.CoreFx.NETCoreApp` and the path to folder with CoreFX NuGet packages. + +Sample arguments: `dotnet run -c Release -f netcoreapp2.1 -- --coreFxBin C:\Projects\forks\corefx\bin\packages\Release --coreFxVersion 4.5.0-preview2-26307-0` + +Sample config: + +```cs +Job.ShortRun.With( + CustomCoreClrToolchain.CreateBuilder() + .UseCoreFxLocalBuild("4.5.0-preview2-26313-0", @"C:\Projects\forks\corefx\bin\packages\Release") + .UseCoreClrDefault() + .AdditionalNuGetFeed("benchmarkdotnet ci", "https://ci.appveyor.com/nuget/benchmarkdotnet"); + .DisplayName("local corefx") + .ToToolchain()); +``` + +### Benchmarking private CoreCLR build + +To run benchmarks with private CoreCLR build you need to provide the version of `Microsoft.NETCore.Runtime`, path to folder with CoreCLR NuGet packages and path to `coreclr\packages` folder. + +Sample arguments: `dotnet run -c Release -f netcoreapp2.1 -- --coreClrBin C:\coreclr\bin\Product\Windows_NT.x64.Release\.nuget\pkg --coreClrPackages C:\Projects\coreclr\packages --coreClrVersion 2.1.0-preview2-26305-0` + +Sample config: + +```cs +Job.ShortRun.With( + CustomCoreClrToolchain.CreateBuilder() + .UseCoreClrLocalBuild("2.1.0-preview2-26313-0", @"C:\Projects\forks\coreclr\bin\Product\Windows_NT.x64.Release\.nuget\pkg", @"C:\Projects\coreclr\packages") + .UseCoreFxDefault() + .AdditionalNuGetFeed("benchmarkdotnet ci", "https://ci.appveyor.com/nuget/benchmarkdotnet"); + .DisplayName("local builds") + .ToToolchain()); +``` + +## Benchmarking private CoreRT build + +To run benchmarks with private CoreRT build you need to provide the `IlcPath`. + +Sample arguments: `dotnet run -c Release -f netcoreapp2.1 -- --ilcPath C:\Projects\corert\bin\Windows_NT.x64.Release` + +Sample config: + +```cs +var config = DefaultConfig.Instance + .With(Job.ShortRun + .With(Runtime.CoreRT) + .With(CoreRtToolchain.CreateBuilder() + .UseCoreRtLocal(@"C:\Projects\corert\bin\Windows_NT.x64.Release") // IlcPath + .DisplayName("Core RT RyuJit") + .ToToolchain())); +``` + diff --git a/src/benchmarks/Serializers/README.md b/src/benchmarks/Serializers/README.md new file mode 100644 index 00000000000..955bf749da2 --- /dev/null +++ b/src/benchmarks/Serializers/README.md @@ -0,0 +1,34 @@ +# Serializers Benchmarks + +This folder contains benchmarks of the most popular serializers. + +## Serializers used (latest stable versions) + +* XML + * System.Xml.XmlSerializer `4.3.0` +* JSON + * System.Runtime.Serialization.Json `4.3.0` + * [Jil](https://github.com/kevin-montrose/Jil) `2.15.4` + * [JSON.NET](https://github.com/JamesNK/Newtonsoft.Json) `11.0.1` + * [Utf8Json](https://github.com/neuecc/Utf8Json) `1.3.7` +* Binary + * BinaryFormatter + * [MessagePack](https://github.com/neuecc/MessagePack-CSharp) `1.7.3.4` + * [protobuff-net](https://github.com/mgravell/protobuf-net) `2.3.7` + * [ZeroFormatter](https://github.com/neuecc/ZeroFormatter) `1.6.4` + +Missing: ProtoBuff from Google and BOND from MS + +## Data Contracts + +Data Contracts were copied from a real Web App – [allReady](https://github.com/HTBox/allReady/) to mimic real world scenarios. + +* [LoginViewModel](DataGenerator.cs#L120) – class, 3 properties +* [Location](DataGenerator.cs#L133) – class, 9 properties +* [IndexViewModel](DataGenerator.cs#L202) – class, nested class + list of 20 Events (8 properties each) +* [MyEventsListerViewModel](DataGenerator.cs#L224) - class, 3 lists of complex types, each type contains another list of complex types + +## Design Decisions + +1. We want to compare "apples to apples", so the benchmarks are divided into few groups: `ToStream`, `FromStream`, `ToString`, `FromString`. +2. Stream benchmarks write to pre-allocated MemoryStream, so the allocated bytes columns include only the cost of serialization. diff --git a/src/benchmarks/img/chooseBenchmark.png b/src/benchmarks/img/chooseBenchmark.png new file mode 100644 index 0000000000000000000000000000000000000000..1114d8c7d4fc4c56a3e1e2343383ca86aa00453d GIT binary patch literal 105693 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�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 literal 0 HcmV?d00001 diff --git a/src/benchmarks/img/exportedResults.png b/src/benchmarks/img/exportedResults.png new file mode 100644 index 0000000000000000000000000000000000000000..f4ddd8c5394098ca266618fb786a905df8254147 GIT binary patch literal 22309 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 literal 0 HcmV?d00001 From ad6b864f3609bab73e1f8fb2550cfe77bcade673 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Wed, 18 Apr 2018 01:26:09 +0200 Subject: [PATCH 10/10] add missing links to the docs + explicit dependency to 4.3.0 of System.Runtime.Serialization.Formatters --- src/benchmarks/Benchmarks.csproj | 1 + src/benchmarks/Serializers/README.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/benchmarks/Benchmarks.csproj b/src/benchmarks/Benchmarks.csproj index b53ad1a8ed2..44b4df6721f 100644 --- a/src/benchmarks/Benchmarks.csproj +++ b/src/benchmarks/Benchmarks.csproj @@ -23,6 +23,7 @@ + diff --git a/src/benchmarks/Serializers/README.md b/src/benchmarks/Serializers/README.md index 955bf749da2..86b425f47ef 100644 --- a/src/benchmarks/Serializers/README.md +++ b/src/benchmarks/Serializers/README.md @@ -5,14 +5,14 @@ This folder contains benchmarks of the most popular serializers. ## Serializers used (latest stable versions) * XML - * System.Xml.XmlSerializer `4.3.0` + * [XmlSerializer](https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlserializer) `4.3.0` * JSON - * System.Runtime.Serialization.Json `4.3.0` + * [DataContractJsonSerializer](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.json.datacontractjsonserializer) `4.3.0` * [Jil](https://github.com/kevin-montrose/Jil) `2.15.4` * [JSON.NET](https://github.com/JamesNK/Newtonsoft.Json) `11.0.1` * [Utf8Json](https://github.com/neuecc/Utf8Json) `1.3.7` * Binary - * BinaryFormatter + * [BinaryFormatter](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter) `4.3.0` * [MessagePack](https://github.com/neuecc/MessagePack-CSharp) `1.7.3.4` * [protobuff-net](https://github.com/mgravell/protobuf-net) `2.3.7` * [ZeroFormatter](https://github.com/neuecc/ZeroFormatter) `1.6.4`