Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more Enum perf tests #2777

Merged
merged 1 commit into from
Jan 9, 2023
Merged
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
162 changes: 154 additions & 8 deletions src/benchmarks/micro/libraries/System.Runtime/Perf.Enum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using BenchmarkDotNet.Attributes;
using MicroBenchmarks;

Expand All @@ -29,17 +32,55 @@ public enum ByteEnum : byte

[Benchmark]
[Arguments(Colors.Yellow)]
public string EnumToString(Colors value) => value.ToString();
[Arguments(Colors.Yellow | Colors.Blue)]
[Arguments(Colors.Red | Colors.Orange | Colors.Yellow | Colors.Green | Colors.Blue)]
[Arguments(Colors.Yellow | (Colors)0x20)]
[Arguments(0x20)]
public string ToString_Flags(Colors value) => value.ToString();

[Benchmark]
[Arguments(SearchOption.TopDirectoryOnly)]
[Arguments(SearchOption.AllDirectories)]
[Arguments((SearchOption)(-1))]
public string ToString_NonFlags_Small(SearchOption value) => value.ToString();

[Benchmark]
[Arguments(UnicodeCategory.UppercaseLetter)]
[Arguments(UnicodeCategory.Control)]
[Arguments(UnicodeCategory.Format)]
[Arguments(UnicodeCategory.OtherNotAssigned)]
[Arguments((UnicodeCategory)42)]
public string ToString_NonFlags_Large(UnicodeCategory value) => value.ToString();

[Benchmark]
[Arguments(DayOfWeek.Sunday, "")]
[Arguments(DayOfWeek.Monday, "g")]
[Arguments(DayOfWeek.Tuesday, "d")]
[Arguments(DayOfWeek.Wednesday, "x")]
[Arguments(DayOfWeek.Thursday, "f")]
[Arguments(DayOfWeek.Friday, "X")]
[Arguments(DayOfWeek.Saturday, "D")]
[Arguments((DayOfWeek)7, "G")]
[Arguments((DayOfWeek)8, "F")]
public string ToString_Format_NonFlags(DayOfWeek value, string format) => value.ToString(format);

[Benchmark]
[Arguments(AttributeTargets.All, "")]
[Arguments(AttributeTargets.All, "g")]
[Arguments(AttributeTargets.All, "d")]
[Arguments(AttributeTargets.All, "x")]
[Arguments(AttributeTargets.All, "f")]
public string ToString_Format_Flags_Large(AttributeTargets value, string format) => value.ToString(format);

[Benchmark]
[Arguments("Red")]
[Arguments("Red, Orange, Yellow, Green, Blue")]
public Colors Parse(string text) => (Colors)Enum.Parse(typeof(Colors), text);
public Colors Parse_Flags(string text) => (Colors)Enum.Parse(typeof(Colors), text);

[Benchmark]
[Arguments("Red")]
[Arguments("Red, Orange, Yellow, Green, Blue")]
public bool TryParseGeneric(string text) => Enum.TryParse<Colors>(text, out _);
public bool TryParseGeneric_Flags(string text) => Enum.TryParse<Colors>(text, out _);

private Colors _greenAndRed = Colors.Green | Colors.Red;

Expand All @@ -49,19 +90,124 @@ public enum ByteEnum : byte
private ByteEnum _byteEnum = ByteEnum.A;

[Benchmark]
public void Compare() => Comparer<ByteEnum>.Default.Compare(_byteEnum, _byteEnum);
public int Compare() => Comparer<ByteEnum>.Default.Compare(_byteEnum, _byteEnum);

[Benchmark]
public string GetName_NonGeneric_Flags() => Enum.GetName(typeof(Colors), Colors.Blue);

[Benchmark]
[Arguments(Colors.Red)]
[Arguments(Colors.Red | Colors.Green)]
[Arguments(0x20)]
public string StringFormat(Colors value) =>
string.Format("{0} {0:d} {0:f} {0:g} {0:x}", value);

[Benchmark]
[Arguments(Colors.Red)]
[Arguments(Colors.Red | Colors.Green)]
[Arguments(0x20)]
public string InterpolateIntoString(Colors value) =>
$"{value} {value:d} {value:f} {value:g} {value:x}";

#if NET5_0_OR_GREATER
private Colors _colorValue = Colors.Blue;
private DayOfWeek _dayOfWeekValue = DayOfWeek.Saturday;

[Benchmark]
public bool IsDefined_Generic_Flags() => Enum.IsDefined(_colorValue);

[Benchmark]
public bool IsDefined_Generic_NonFlags() => Enum.IsDefined(_dayOfWeekValue);

[Benchmark]
public string GetName_Generic_Flags() => Enum.GetName(_colorValue);

[Benchmark]
public string GetName_Generic_NonFlags() => Enum.GetName(_dayOfWeekValue);

[Benchmark]
public bool IsDefined() => Enum.IsDefined(_colorValue);
public string[] GetNames_Generic() => Enum.GetNames<Colors>();

[Benchmark]
public Colors[] GetValues_Generic() => Enum.GetValues<Colors>();
#endif

#if NET6_0_OR_GREATER
// TODO: https://github.com/dotnet/performance/issues/2776
// These tests use manual invocations of the relevant APIs rather than using
// language syntax for string interpolation as doing so would require C# 10
// and the repo is currently pinned to C# 9. Once that limit is lifted, these
// tests should switch to testing the language syntax.

private static readonly char[] s_scratch = new char[512];

[Benchmark]
[Arguments(Colors.Red)]
[Arguments(Colors.Red | Colors.Green)]
[Arguments(0x20)]
public bool InterpolateIntoSpan_Flags(Colors value) => InterpolateIntoSpan(s_scratch, value);

[Benchmark]
[Arguments(DayOfWeek.Saturday)]
[Arguments(42)]
public bool InterpolateIntoSpan_NonFlags(Colors value) => InterpolateIntoSpan(s_scratch, value);

private static bool InterpolateIntoSpan<T>(Span<char> span, T value)
{
// span.TryWrite($"{value} {value:d} {value:f} {value:g} {value:x}")
var handler = new MemoryExtensions.TryWriteInterpolatedStringHandler(4, 5, span, out bool shouldAppend);
return
shouldAppend &&
handler.AppendFormatted(value) &&
handler.AppendLiteral(" ") &&
handler.AppendFormatted(value, "d") &&
handler.AppendLiteral(" ") &&
handler.AppendFormatted(value, "f") &&
handler.AppendLiteral(" ") &&
handler.AppendFormatted(value, "g") &&
handler.AppendLiteral(" ") &&
handler.AppendFormatted(value, "x") &&
MemoryExtensions.TryWrite(span, ref handler, out _);
}

private static StringBuilder s_sb = new StringBuilder();

[Benchmark]
[Arguments(Colors.Red)]
[Arguments(Colors.Red | Colors.Green)]
[Arguments(0x20)]
public void InterpolateIntoStringBuilder_Flags(Colors value) => InterpolateIntoStringBuilder(s_sb, value);

[Benchmark]
[Arguments(DayOfWeek.Saturday)]
[Arguments(42)]
public void InterpolateIntoStringBuilder_NonFlags(Colors value) => InterpolateIntoStringBuilder(s_sb, value);

private static void InterpolateIntoStringBuilder<T>(StringBuilder sb, T value)
{
sb.Clear();

// sb.Append($"{value} {value:d} {value:f} {value:g} {value:x}")
var handler = new StringBuilder.AppendInterpolatedStringHandler(4, 5, sb, null);
handler.AppendFormatted(value);
handler.AppendLiteral(" ");
handler.AppendFormatted(value, "d");
handler.AppendLiteral(" ");
handler.AppendFormatted(value, "f");
handler.AppendLiteral(" ");
handler.AppendFormatted(value, "g");
handler.AppendLiteral(" ");
handler.AppendFormatted(value, "x");
sb.Append(ref handler);
}
#endif

#if NET7_0_OR_GREATER
[Benchmark]
public string GetName() => Enum.GetName(_colorValue);
public Array GetValuesAsUnderlyingType_Generic() => Enum.GetValuesAsUnderlyingType<UnicodeCategory>();

[Benchmark]
public string[] GetNames() => Enum.GetNames<Colors>();
public Array GetValuesAsUnderlyingType_NonGeneric() => Enum.GetValuesAsUnderlyingType(typeof(UnicodeCategory));
#endif
}
}
}