【发布时间】:2011-11-14 04:22:13
【问题描述】:
我正在做一些速度测试,我注意到 Enum.HasFlag 比使用按位运算慢了大约 16 倍。
有谁知道 Enum.HasFlag 的内部结构以及它为什么这么慢?我的意思是慢两倍不会太糟糕,但是当它慢 16 倍时它会导致函数无法使用。
如果有人想知道,这是我用来测试其速度的代码。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace app
{
public class Program
{
[Flags]
public enum Test
{
Flag1 = 1,
Flag2 = 2,
Flag3 = 4,
Flag4 = 8
}
static int num = 0;
static Random rand;
static void Main(string[] args)
{
int seed = (int)DateTime.UtcNow.Ticks;
var st1 = new SpeedTest(delegate
{
Test t = Test.Flag1;
t |= (Test)rand.Next(1, 9);
if (t.HasFlag(Test.Flag4))
num++;
});
var st2 = new SpeedTest(delegate
{
Test t = Test.Flag1;
t |= (Test)rand.Next(1, 9);
if (HasFlag(t , Test.Flag4))
num++;
});
rand = new Random(seed);
st1.Test();
rand = new Random(seed);
st2.Test();
Console.WriteLine("Random to prevent optimizing out things {0}", num);
Console.WriteLine("HasFlag: {0}ms {1}ms {2}ms", st1.Min, st1.Average, st1.Max);
Console.WriteLine("Bitwise: {0}ms {1}ms {2}ms", st2.Min, st2.Average, st2.Max);
Console.ReadLine();
}
static bool HasFlag(Test flags, Test flag)
{
return (flags & flag) != 0;
}
}
[DebuggerDisplay("Average = {Average}")]
class SpeedTest
{
public int Iterations { get; set; }
public int Times { get; set; }
public List<Stopwatch> Watches { get; set; }
public Action Function { get; set; }
public long Min { get { return Watches.Min(s => s.ElapsedMilliseconds); } }
public long Max { get { return Watches.Max(s => s.ElapsedMilliseconds); } }
public double Average { get { return Watches.Average(s => s.ElapsedMilliseconds); } }
public SpeedTest(Action func)
{
Times = 10;
Iterations = 100000;
Function = func;
Watches = new List<Stopwatch>();
}
public void Test()
{
Watches.Clear();
for (int i = 0; i < Times; i++)
{
var sw = Stopwatch.StartNew();
for (int o = 0; o < Iterations; o++)
{
Function();
}
sw.Stop();
Watches.Add(sw);
}
}
}
}
结果:
HasFlag: 52ms 53.6ms 55ms
Bitwise: 3ms 3ms 3ms
【问题讨论】:
-
因为枚举类型可以有不同的底层基类型。 Enum.HasValue 不能对该基本类型做出任何假设,它必须假设最坏的情况。这涉及使用 UInt64 和盒装值。您的 HashType 函数是类型安全的。
-
你可能还想看看这个:why-enums-hasflag-method-need-boxing
-
我刚刚使用 .NET 4.6 对此进行了基准测试:
HasFlag: 8ms 8,7ms 11ms、Bitwise: 4ms 4ms 4ms。所以他们似乎改进了实施。 -
在 .NET Fiddle 中使用 4.7.2 进行基准测试 HasFlag:8ms 9.4ms 16ms 按位:5ms 5ms 5ms .NET Core 2.2 更糟:HasFlag:17ms 22.7ms 26ms 按位:9ms 10.2ms 15ms跨度>