Skip to content

Conversation

@stephentoub
Copy link
Member

Today when doing Enum.IsDefined, Enum.GetName, or Enum.ToString, a cache of an enum's values is binary searched to find the matching entry. But for enums with a smaller number of values, it's cheaper to do a linear search.

Method Toolchain dow Mean Ratio
EnumToString \main\corerun.exe Monday 18.83 ns 1.00
EnumToString \pr\corerun.exe Monday 15.30 ns 0.81
EnumToString \main\corerun.exe Wednesday 15.81 ns 1.00
EnumToString \pr\corerun.exe Wednesday 14.36 ns 0.91
EnumToString \main\corerun.exe Friday 17.96 ns 1.00
EnumToString \pr\corerun.exe Friday 15.33 ns 0.85
Benchmark code
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;

public class Program
{
    public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);

    [Benchmark]
    [Arguments(DayOfWeek.Monday)]
    [Arguments(DayOfWeek.Wednesday)]
    [Arguments(DayOfWeek.Friday)]
    public string EnumToString(DayOfWeek dow) => dow.ToString();
}
Method Toolchain value Mean Ratio
One_IsDefined \main\corerun.exe Value0 16.74 ns 1.00
One_IsDefined \pr\corerun.exe Value0 14.14 ns 0.84
One_IsDefined \main\corerun.exe 12345 17.61 ns 1.00
One_IsDefined \pr\corerun.exe 12345 13.87 ns 0.78
Sixteen_IsDefined \main\corerun.exe Value0 23.85 ns 1.00
Sixteen_IsDefined \pr\corerun.exe Value0 13.03 ns 0.55
Sixteen_IsDefined \main\corerun.exe Value7 16.77 ns 1.00
Sixteen_IsDefined \pr\corerun.exe Value7 14.31 ns 0.85
Sixteen_IsDefined \main\corerun.exe Value8 22.98 ns 1.00
Sixteen_IsDefined \pr\corerun.exe Value8 15.22 ns 0.66
Sixteen_IsDefined \main\corerun.exe Value15 23.03 ns 1.00
Sixteen_IsDefined \pr\corerun.exe Value15 15.83 ns 0.69
Sixteen_IsDefined \main\corerun.exe 12345 23.92 ns 1.00
Sixteen_IsDefined \pr\corerun.exe 12345 16.19 ns 0.68
ThirtyTwo_IsDefined \main\corerun.exe Value0 28.98 ns 1.00
ThirtyTwo_IsDefined \pr\corerun.exe Value0 21.29 ns 0.73
ThirtyTwo_IsDefined \main\corerun.exe Value15 19.13 ns 1.00
ThirtyTwo_IsDefined \pr\corerun.exe Value15 16.25 ns 0.85
ThirtyTwo_IsDefined \main\corerun.exe Value16 28.49 ns 1.00
ThirtyTwo_IsDefined \pr\corerun.exe Value16 22.04 ns 0.77
ThirtyTwo_IsDefined \main\corerun.exe Value31 17.25 ns 1.00
ThirtyTwo_IsDefined \pr\corerun.exe Value31 14.37 ns 0.84
ThirtyTwo_IsDefined \main\corerun.exe 12345 26.78 ns 1.00
ThirtyTwo_IsDefined \pr\corerun.exe 12345 25.58 ns 0.95
SixtyFour_IsDefined \main\corerun.exe Value0 28.93 ns 1.00
SixtyFour_IsDefined \pr\corerun.exe Value0 21.23 ns 0.73
SixtyFour_IsDefined \main\corerun.exe Value31 17.21 ns 1.00
SixtyFour_IsDefined \pr\corerun.exe Value31 14.07 ns 0.82
SixtyFour_IsDefined \main\corerun.exe Value32 28.47 ns 1.00
SixtyFour_IsDefined \pr\corerun.exe Value32 21.64 ns 0.76
SixtyFour_IsDefined \main\corerun.exe Value63 26.18 ns 1.00
SixtyFour_IsDefined \pr\corerun.exe Value63 26.18 ns 1.00
SixtyFour_IsDefined \main\corerun.exe 12345 26.26 ns 1.00
SixtyFour_IsDefined \pr\corerun.exe 12345 25.31 ns 0.96
Benchmark code
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;

public class Program
{
    public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);

    [Benchmark]
    [Arguments(EnumWithOneValue.Value0)]
    [Arguments((EnumWithOneValue)12345)]
    public bool One_IsDefined(EnumWithOneValue value) => Enum.IsDefined(value);

    [Benchmark]
    [Arguments(EnumWithSixteenValues.Value0)]
    [Arguments(EnumWithSixteenValues.Value7)]
    [Arguments(EnumWithSixteenValues.Value8)]
    [Arguments(EnumWithSixteenValues.Value15)]
    [Arguments((EnumWithSixteenValues)12345)]
    public bool Sixteen_IsDefined(EnumWithSixteenValues value) => Enum.IsDefined(value);

    [Benchmark]
    [Arguments(EnumWithThirtyTwoValues.Value0)]
    [Arguments(EnumWithThirtyTwoValues.Value15)]
    [Arguments(EnumWithThirtyTwoValues.Value16)]
    [Arguments(EnumWithThirtyTwoValues.Value31)]
    [Arguments((EnumWithThirtyTwoValues)12345)]
    public bool ThirtyTwo_IsDefined(EnumWithSixtyFourValues value) => Enum.IsDefined(value);

    [Benchmark]
    [Arguments(EnumWithSixtyFourValues.Value0)]
    [Arguments(EnumWithSixtyFourValues.Value31)]
    [Arguments(EnumWithSixtyFourValues.Value32)]
    [Arguments(EnumWithSixtyFourValues.Value63)]
    [Arguments((EnumWithSixtyFourValues)12345)]
    public bool SixtyFour_IsDefined(EnumWithSixtyFourValues value) => Enum.IsDefined(value);

    public enum EnumWithOneValue
    {
        Value0,
    }

    public enum EnumWithSixteenValues
    {
        Value0,
        Value1,
        Value2,
        Value3,
        Value4,
        Value5,
        Value6,
        Value7,
        Value8,
        Value9,
        Value10,
        Value11,
        Value12,
        Value13,
        Value14,
        Value15,
    }

    public enum EnumWithThirtyTwoValues
    {
        Value0,
        Value1,
        Value2,
        Value3,
        Value4,
        Value5,
        Value6,
        Value7,
        Value8,
        Value9,
        Value10,
        Value11,
        Value12,
        Value13,
        Value14,
        Value15,
        Value16,
        Value17,
        Value18,
        Value19,
        Value20,
        Value21,
        Value22,
        Value23,
        Value24,
        Value25,
        Value26,
        Value27,
        Value28,
        Value29,
        Value30,
        Value31,
    }

    public enum EnumWithSixtyFourValues
    {
        Value0,
        Value1,
        Value2,
        Value3,
        Value4,
        Value5,
        Value6,
        Value7,
        Value8,
        Value9,
        Value10,
        Value11,
        Value12,
        Value13,
        Value14,
        Value15,
        Value16,
        Value17,
        Value18,
        Value19,
        Value20,
        Value21,
        Value22,
        Value23,
        Value24,
        Value25,
        Value26,
        Value27,
        Value28,
        Value29,
        Value30,
        Value31,
        Value32,
        Value33,
        Value34,
        Value35,
        Value36,
        Value37,
        Value38,
        Value39,
        Value40,
        Value41,
        Value42,
        Value43,
        Value44,
        Value45,
        Value46,
        Value47,
        Value48,
        Value49,
        Value50,
        Value51,
        Value52,
        Value53,
        Value54,
        Value55,
        Value56,
        Value57,
        Value58,
        Value59,
        Value60,
        Value61,
        Value62,
        Value63,
    }
}

@ghost
Copy link

ghost commented Aug 23, 2021

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

Today when doing Enum.IsDefined, Enum.GetName, or Enum.ToString, a cache of an enum's values is binary searched to find the matching entry.  But for enums with a smaller number of values, it's cheaper to do a linear search.
@stephentoub
Copy link
Member Author

Thanks, @GrabYourPitchforks. Updated. Seems to maybe have yielded an additional 1-2%.

@stephentoub stephentoub merged commit 40575af into dotnet:main Aug 27, 2021
@stephentoub stephentoub deleted the enumsearch branch August 27, 2021 10:50
@ghost ghost locked as resolved and limited conversation to collaborators Sep 26, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants