是什么力量使Enum.HasFlag这么慢? [英] What is it that makes Enum.HasFlag so slow?

查看:315
本文介绍了是什么力量使Enum.HasFlag这么慢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在做一些速度测试中,我注意到,Enum.HasFlag比使用按位运算慢16倍左右。

有谁知道Enum.HasFlag的内部结构以及它为什么这么慢?我的意思是慢一倍不会太糟糕,但它使功能不可用时,它的16倍慢。

在情况下,你想知道,这里是code我使用来测试其速度。

 使用系统;
使用System.Collections.Generic;
使用System.Diagnostics程序;
使用System.Linq的;

命名空间的应用程序
{
    公共类节目
    {
        [国旗]
        公共枚举测试
        {
            FLAG1 = 1,
            标志2 = 2,
            相当于Flag3 = 4,
            Flag4的= 8
        }
        静态INT NUM = 0;
        静态随机兰特;
        静态无效的主要(字串[] args)
        {
            INT种子=(INT)DateTime.UtcNow.Ticks;

            VAR ST1 =新SPEEDTEST(委托
            {
                测试T = Test.Flag1;
                T | =(测试)rand.Next(1,9);
                如果(t.HasFlag(Test.Flag4))
                    NUM ++;
            });

            VAR ST2 =新SPEEDTEST(委托
            {
                测试T = Test.Flag1;
                T | =(测试)rand.Next(1,9);
                如果(HasFlag(T,Test.Flag4))
                    NUM ++;
            });

            兰特=新的随机(种子);
            st1.Test();
            兰特=新的随机(种子);
            st2.Test();

            Console.WriteLine(随机prevent优化出来的东西{0},NUM);
            Console.WriteLine(HasFlag:{0}毫秒{1}毫秒{2}毫秒,st1.Min,st1.Average,st1.Max);
            Console.WriteLine(按位:{0}毫秒{1}毫秒{2}毫秒,st2.Min,st2.Average,st2.Max);
            到Console.ReadLine();
        }
        静态布尔HasFlag(测试标志,测试标志)
        {
            返程(旗&安培;标志)!= 0;
        }
    }
    [DebuggerDisplay(平均= {}平均值)]
    类SPEEDTEST
    {
        公众诠释迭代{获得;组; }

        公众诠释时报{获得;组; }

        公开名单<秒表>手表{获得;组; }

        公开操作功能{获得;组; }

        众长敏{{返回Watches.Min(S => s.ElapsedMilliseconds); }}

        众长最大值{{返回Watches.Max(S => s.ElapsedMilliseconds); }}

        公共双平均{{返回Watches.Average(S => s.ElapsedMilliseconds); }}

        公共SPEEDTEST(动作FUNC)
        {
            次= 10;
            迭代= 100000;
            功能= FUNC;
            手表=新的名单,其中,秒表>();
        }

        公共无效测试()
        {
            Watches.Clear();
            的for(int i = 0; I<时代;我++)
            {
                变种SW = Stopwatch.StartNew();
                对于(INT O = 0; O<迭代;○++)
                {
                    功能();
                }
                sw.Stop();
                Watches.Add(SW);
            }
        }
    }

}
 

结果: HasFlag:52ms 53.6ms的55ms 按位:3毫秒3毫秒3毫秒

解决方案
  

有谁知道Enum.HasFlag的内部结构以及它为什么这么慢?

实际的检查只是一个简单的位检查在 Enum.HasFlag - 这不是这里的问题。话虽这么说,这是不是你自己的位检查慢...

有几个原因放缓:

首先, Enum.HasFlag 做一个显式检验,以确保该枚举的类型和标志的类型都是相同的类型,并从相同的枚举。有一个在这个检查一定的成本。

其次,有发生 HasFlag UINT64 在一个不幸箱和价值拆箱>。这是,我认为,由于该 Enum.HasFlag 所有枚举,而不管底层的存储类型的要求。

话虽这么说,有一个巨大的优势, Enum.HasFlag - 它的可靠,清洁,并使得code非常明显和前pressive 。在大多数情况下,我觉得这使得它值得的成本 - 但如果你在一个非常关键的性能循环利用这一点,它可能是值得做你自己检查

I was doing some speed tests and I noticed that Enum.HasFlag is about 16 times slower than using the bitwise operation.

Does anyone know the internals of Enum.HasFlag and why it is so slow? I mean twice as slow wouldn't be too bad but it makes the function unusable when its 16 times slower.

In case anyone is wondering, here is the code I am using to test its speed.

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);
            }
        }
    }

}

Results: HasFlag: 52ms 53.6ms 55ms Bitwise: 3ms 3ms 3ms

解决方案

Does anyone know the internals of Enum.HasFlag and why it is so slow?

The actual check is just a simple bit check in Enum.HasFlag - it's not the problem here. That being said, it is slower than your own bit check...

There are a couple of reasons for this slowdown:

First, Enum.HasFlag does an explicit check to make sure that the type of the enum and the type of the flag are both the same type, and from the same Enum. There is some cost in this check.

Secondly, there is an unfortunate box and unbox of the value during a conversion to UInt64 that occurs inside of HasFlag. This is, I believe, due to the requirement that Enum.HasFlag work with all enums, regardless of the underlying storage type.

That being said, there is a huge advantage to Enum.HasFlag - it's reliable, clean, and makes the code very obvious and expressive. For the most part, I feel that this makes it worth the cost - but if you're using this in a very performance critical loop, it may be worth doing your own check.

这篇关于是什么力量使Enum.HasFlag这么慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆