Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions PerformanceStubs/Core/PerformanceTestOneInOneOut.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ public abstract class PerformanceTestOneInOneOut<TInput1, TResult> : IPerformanc
protected abstract string Title { get; }
protected abstract string Caption { get; }

private PerformanceTestCandidateResult Run(Func<TInput1, TResult> operation, TInput1 victim, TResult expectedOutput, long iterations) {
private PerformanceTestCandidateResult Run(Func<TInput1, TResult> operation, TInput1 victim, Func<TInput1, TResult> typicalCandidate, TResult expectedOutput, long iterations) {
TResult testResult = operation(victim); // also primes anything that may be a one-time cost
Debug.Assert(this.OutputComparer(expectedOutput, testResult));
if (!this.OutputComparer(expectedOutput, testResult))
{
throw new InvalidOperationException(
$"{operation.Method.Name} did not return the same as {typicalCandidate.Method.Name}. {testResult} != {expectedOutput}.");
}

List<long> elapsedTicksCollection = new List<long>();
Stopwatch timer = new Stopwatch();
Expand All @@ -35,9 +39,10 @@ public PerformanceTestSummary Run() {
return testSummary;
}

TResult typicalAnswer = this.TestCandidates.First()(this.Input1);
var typicalCandidate = this.TestCandidates.First();
TResult typicalAnswer = typicalCandidate(this.Input1);
foreach (Func<TInput1, TResult> testCandidate in this.TestCandidates) {
PerformanceTestCandidateResult candidateResult = this.Run(testCandidate, this.Input1, typicalAnswer, this.Iterations);
PerformanceTestCandidateResult candidateResult = this.Run(testCandidate, this.Input1, typicalCandidate, typicalAnswer, this.Iterations);
candidateResults.Add(candidateResult);
}
return testSummary;
Expand Down
94 changes: 10 additions & 84 deletions PerformanceStubs/PerformanceStubs.csproj
Original file line number Diff line number Diff line change
@@ -1,84 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{33EC6BFE-BD71-4054-9B50-D047ADA0C5E5}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PerformanceStubs</RootNamespace>
<AssemblyName>PerformanceStubs</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TargetFrameworks>net48;net9.0</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="FastMember, Version=1.0.0.9, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\FastMember.1.0.0.9\lib\net40\FastMember.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
<Reference Include="System.Web" />
<Reference Include="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Core\AssemblyExtensions.cs" />
<Compile Include="Core\FluentTagBuilder.cs" />
<Compile Include="Core\IEnumerableExtensions.cs" />
<Compile Include="Core\PerformanceTestOneInOneOut.cs" />
<Compile Include="Core\PerformanceTestSummary.cs" />
<Compile Include="Core\PerformanceTestCandidateResult.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Core\IPerformanceTest.cs" />
<Compile Include="Tests\ConvertByteArrayToHexString\Test.cs" />
<Compile Include="Tests\GettingObjectPropertiesByName\Something.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Tests\GettingObjectPropertiesByName\Test.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Tests\ListFirstSubtypeItem\Something.cs" />
<Compile Include="Tests\ListFirstSubtypeItem\SubSomething.cs" />
<Compile Include="Tests\ListFirstSubtypeItem\Test.cs" />
<Compile Include="Tests\ListSubsetCasting\Something.cs" />
<Compile Include="Tests\ListSubsetCasting\SubSomething.cs" />
<Compile Include="Tests\ListSubsetCasting\Test.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Tests\ConvertByteArrayToHexString\LargeText.txt">
Expand All @@ -88,12 +16,10 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<ItemGroup>
<PackageReference Include="FastMember" Version="1.5.0" />
<PackageReference Include="Microsoft.AspNet.WebPages" Version="3.3.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.3.0" />
<PackageReference Include="System.Management" Version="9.0.4" />
</ItemGroup>
</Project>
80 changes: 78 additions & 2 deletions PerformanceStubs/Tests/ConvertByteArrayToHexString/Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
#if NETFRAMEWORK
using System.Runtime.Remoting.Metadata.W3cXsd2001;
#endif
using System.Text;
using PerformanceStubs.Core;

Expand All @@ -22,36 +24,108 @@ protected override string Caption {
protected override List<Func<byte[], string>> TestCandidates {
get {
return (new Func<byte[], string>[] {
#if NET5_0_OR_GREATER
ByteArrayToHexStringViaConvertToHexString,
#endif
#if NET9_0_OR_GREATER
ByteArrayToHexStringViaStringCreateConvertTryToHexString,
#endif
ByteArrayToHexStringViaStringJoinArrayConvertAll,
ByteArrayToHexStringViaStringConcatArrayConvertAll,
ByteArrayToHexStringViaBitConverter,
#if NETCOREAPP2_1_OR_GREATER
ByteArrayToHexStringViaStringCreateByteTryFormat,
#endif
#if NET9_0_OR_GREATER
ByteArrayToHexStringViaStringCreateLoopConvertTryToHexString,
#endif
ByteArrayToHexStringViaBitConverterThenReplace,

#if NETCOREAPP2_1_OR_GREATER
ByteArrayToHexStringViaBitConverterThenCopy,
#endif
ByteArrayToHexStringViaStringBuilderAggregateByteToString,
ByteArrayToHexStringViaStringBuilderAggregateByteInterpolation,
ByteArrayToHexStringViaStringBuilderAggregateAppendFormat,
ByteArrayToHexStringViaStringBuilderForEachByteToString,
ByteArrayToHexStringViaStringBuilderForEachAppendFormat,
ByteArrayToHexViaByteManipulation,
ByteArrayToHexViaByteManipulation2,
#if NETFRAMEWORK
ByteArrayToHexViaSoapHexBinary,
#endif
ByteArrayToHexViaLookupAndShift,
ByteArrayToHexViaLookup,
ByteArrayToHexViaLookupPerByte,
ByteArrayToHexViaLookup32UnsafeDirect,
}).ToList();
}
}
#if NET5_0_OR_GREATER
static string ByteArrayToHexStringViaConvertToHexString(byte[] bytes) {
return Convert.ToHexString(bytes);
}
#endif
#if NET9_0_OR_GREATER
static string ByteArrayToHexStringViaStringCreateConvertTryToHexString(byte[] bytes) {
return string.Create(bytes.Length * 2, bytes, static (span, bytes) => Convert.TryToHexString(bytes, span, out _));
}
#endif
static string ByteArrayToHexStringViaStringJoinArrayConvertAll(byte[] bytes) {
return string.Join(string.Empty, Array.ConvertAll(bytes, b => b.ToString("X2")));
}
static string ByteArrayToHexStringViaStringConcatArrayConvertAll(byte[] bytes) {
return string.Concat(Array.ConvertAll(bytes, b => b.ToString("X2")));
}
static string ByteArrayToHexStringViaBitConverter(byte[] bytes) {
#if NETCOREAPP2_1_OR_GREATER
static string ByteArrayToHexStringViaStringCreateByteTryFormat(byte[] bytes) {
return string.Create(bytes.Length * 2, bytes, static (span, bytes) =>
{
for (var i = 0; i < bytes.Length; i++)
{
bytes[i].TryFormat(span.Slice(i * 2, 2), out _, "X2");
}
});
}
#endif
#if NET9_0_OR_GREATER
static string ByteArrayToHexStringViaStringCreateLoopConvertTryToHexString(byte[] bytes) {
return string.Create(bytes.Length * 2, bytes, static (span, bytes) =>
{
for (var i = 0; i < bytes.Length; i++)
{
Convert.TryToHexString(bytes.AsSpan(i, 1), span.Slice(i * 2, 2), out _);
}
});
}
#endif
static string ByteArrayToHexStringViaBitConverterThenReplace(byte[] bytes) {
string hex = BitConverter.ToString(bytes);
return hex.Replace("-", "");
}
#if NETCOREAPP2_1_OR_GREATER
static string ByteArrayToHexStringViaBitConverterThenCopy(byte[] bytes) {
if (bytes.Length < 2)
{
return BitConverter.ToString(bytes);
}
return string.Create(bytes.Length * 2, bytes, static (span, bytes) =>
{
string hex = BitConverter.ToString(bytes);
for (int i = 0; i < bytes.Length; i++)
{
span[i * 2 + 0] = hex[i * 3 + 0];
span[i * 2 + 1] = hex[i * 3 + 1];
}
});
}
#endif
static string ByteArrayToHexStringViaStringBuilderAggregateByteToString(byte[] bytes) {
return bytes.Aggregate(new StringBuilder(bytes.Length * 2), (sb, b) => sb.Append(b.ToString("X2"))).ToString();
}
static string ByteArrayToHexStringViaStringBuilderAggregateByteInterpolation(byte[] bytes) {
// Syntax-Compatible to .NET Framework, but uses Interpolation-Handler starting with .NET 6
return bytes.Aggregate(new StringBuilder(bytes.Length * 2), (sb, b) => sb.Append($"{b:X2}")).ToString();
}
static string ByteArrayToHexStringViaStringBuilderForEachByteToString(byte[] bytes) {
StringBuilder hex = new StringBuilder(bytes.Length * 2);
foreach (byte b in bytes)
Expand Down Expand Up @@ -92,10 +166,12 @@ static string ByteArrayToHexViaByteManipulation2(byte[] bytes) {
}
return new string(c);
}
#if NETFRAMEWORK
static string ByteArrayToHexViaSoapHexBinary(byte[] bytes) {
SoapHexBinary soapHexBinary = new SoapHexBinary(bytes);
return soapHexBinary.ToString();
}
#endif

const string hexAlphabet = "0123456789ABCDEF";
static string ByteArrayToHexViaLookupAndShift(byte[] bytes) {
Expand Down
16 changes: 12 additions & 4 deletions PerformanceStubs/Tests/GettingObjectPropertiesByName/Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
using PerformanceStubs.Core;
using System.Web.Routing;
using FastMember;
using System.Dynamic;

Expand All @@ -21,13 +20,22 @@ protected override string Caption {
protected override List<Func<object, Tuple<string, int?>>> TestCandidates {
get {
return (new Func<object, Tuple<string, int?>>[] {
IDictionaryRouteValueDictionaryLookup,
#if NETFRAMEWORK
IDictionarySystemWebRouteValueDictionaryLookup,
#endif
IDictionaryAspNetCoreRouteValueDictionaryLookup,
ObjectAccessorLookup
}).ToList();
}
}
public static Tuple<string, int?> IDictionaryRouteValueDictionaryLookup(object source) {
IDictionary<string, object> dictionarySource = source as IDictionary<string, object> ?? new RouteValueDictionary(source);
#if NETFRAMEWORK
public static Tuple<string, int?> IDictionarySystemWebRouteValueDictionaryLookup(object source) {
IDictionary<string, object> dictionarySource = source as IDictionary<string, object> ?? new System.Web.Routing.RouteValueDictionary(source);
return new Tuple<string, int?>(dictionarySource["Id"] as string, dictionarySource["Value"] as int?);
}
#endif
public static Tuple<string, int?> IDictionaryAspNetCoreRouteValueDictionaryLookup(object source) {
IDictionary<string, object> dictionarySource = source as IDictionary<string, object> ?? new Microsoft.AspNetCore.Routing.RouteValueDictionary(source);
return new Tuple<string, int?>(dictionarySource["Id"] as string, dictionarySource["Value"] as int?);
}
public static Tuple<string, int?> ObjectAccessorLookup(object source) {
Expand Down
4 changes: 0 additions & 4 deletions PerformanceStubs/packages.config

This file was deleted.