【问题标题】:C# Enum.HasFlag vs. Bitwise AND Operator CheckC# Enum.HasFlag 与按位与运算符检查
【发布时间】:2016-09-14 15:08:43
【问题描述】:

如果您有一个用于位标志的enum,即,

[Flags]
internal enum _flagsEnum : byte
{
    None = 0,           //00000000
    Option1 = 1,        //00000001
    Option2 = 1 << 1,   //00000010
    Option3 = 1 << 2,   //00000100
    Option4 = 1 << 3,   //00001000
    Option5 = 1 << 4,   //00010000
    Option6 = 1 << 5,   //00100000
    Option7 = 1 << 6,   //01000000
    Option8 = 1 << 7,   //10000000
    All = Byte.MaxValue,//11111111
}

_flagsEnum myFlagsEnum = _flagsEnum.None;

做起来更快吗..

bool hasFlag = myFlagsEnum.HasFlag(_flagsEnum.Option1);

或者去做..

bool hasFlag = myFlagsEnum & _flagsEnum.Option1 != 0

如果检查多个标志之间存在性能差异,那么也要考虑到这一点。

通常我会查看参考源,但在这种情况下,Enum.HasFlags 只是转到外部 InternalHasFlags,所以我不知道它在做什么。

【问题讨论】:

  • 有一些性能成本。见:stackoverflow.com/questions/7368652/…
  • 似乎过早的优化。我不喜欢HasFlag 的一件事是它接受任何enum 对象作为参数,而不是同一类型。
  • HasFlag 不仅仅是简单的按位检查blogs.microsoft.co.il/bnaya/2011/01/28/enumhasflag-good-or-bad
  • @Yushatak 然后我建议您制作自己的扩展方法来为您的枚举重载HasFlag()
  • & 运算符采用单处理器指令 (TEST),在现代 CPU 上运行 0.25 到 1 个处理器周期。它永远不会比调用 CLR 慢。如果您关心速度,那么永远不要使用 byte 作为枚举基本类型,默认 (int) 是最快的。

标签: c# performance optimization enums bit-manipulation


【解决方案1】:

使用HasFlag 会产生性能成本,因为实现会验证您传递的enum 值与标志的类型相同。

消除了这种差异,实现高度优化以避免将较短的类型(例如byte)提升为int

switch (pMTThis->GetNumInstanceFieldBytes()) {
case 1:
    cmp = ((*(UINT8*)pThis & *(UINT8*)pFlags) == *(UINT8*)pFlags);
    break;
case 2:
    cmp = ((*(UINT16*)pThis & *(UINT16*)pFlags) == *(UINT16*)pFlags);
    break;
case 4:
    cmp = ((*(UINT32*)pThis & *(UINT32*)pFlags) == *(UINT32*)pFlags);
    break;
case 8:
    cmp = ((*(UINT64*)pThis & *(UINT64*)pFlags) == *(UINT64*)pFlags);
    break;
default:
    // should not reach here.
    UNREACHABLE_MSG("Incorrect Enum Type size!");
    break;
}

ReflectionEnum::InternalHasFlag的来源可以找到here

虽然成本相对较高,但不太重要,除非是最极端的情况。我建议保留它,除非你的分析器指出这个调用是你程序中最大的瓶颈。

【讨论】:

  • 啊,是的,我应该考虑检查一下现在开放的 CLR 源代码 - 想我还没有习惯!
  • 此外,由于HasFlag 方法将Enum 作为一个类参数,因此正在进行装箱和拆箱。
【解决方案2】:

不安全

这个怎么样?在我的基准测试中,比 HasFlag 快约 25%,比按位慢约 10%-15%,但通用。

也许有人可以优化它。

[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static unsafe Boolean HasFlags<T>(T* first, T* second) where T : unmanaged, Enum
{
    Byte* pf = (Byte*) first;
    Byte* ps = (Byte*) second;

    for (Int32 i = 0; i < sizeof(T); i++)
    {
        if ((pf[i] & ps[i]) != ps[i])
        {
                return false;
        }
    }

    return true;
}
    
/// <remarks>Faster analog of Enum.HasFlag</remarks>
/// <inheritdoc cref="Enum.HasFlag"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Boolean HasFlags<T>(this T first, T second) where T : unmanaged, Enum
{
    return HasFlags(&first, &second);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-28
    • 2012-09-06
    • 1970-01-01
    • 2011-03-26
    • 2021-11-04
    • 2016-04-12
    • 2016-06-24
    相关资源
    最近更新 更多