漫坊亭

社会の底辺プログラマ

Enum.HasFlagの速度

google先生が、Enum.HasFlagはBoxing/Unboxingが発生するので遅い、とおっしゃるので、速度を計ってみた。ついでに、Parallelも使ってみた。

for版

回 数 bit演算 HasFlag 総検査時間
1,000,000 18 148 1,673
10,000,000 169 1,299 14,689
100,000,000 1,688 12,756 144,267

総じて、8倍くらいの時間がかかるようだ。

Parallelで回してみると、総検査時間が1/3くらいになる。
bit演算とHasFlagの速度比が、4倍くらいに縮まっているのは謎だが。

Parallel.For版

回 数 bit演算 HasFlag 総検査時間
1,000,000 14 55 696
10,000,000 106 492 5,552
100,000,000 835 3,501 47,213
  • for版のタスクマネージャ f:id:jfactory:20160204140148p:plain

  • Parallel.For版のタスクマネージャ f:id:jfactory:20160204140204p:plain

Parallelのほうが、CPUをまんべんなく使っているのがわかる。

namespace EnumHasFlagTest
{
    [Flags]
    public enum TestFlag
    {
        A = 1,
        B = 2,
        C = 4,
        D = 8, 
    }

    class Program
    {
        static long Meas(int count, Action func)
        {
            var sw = Stopwatch.StartNew();
            for (int i = 0; i < count; i++)
            {
                func();
            }

            return sw.ElapsedMilliseconds;
        }

        static void FuncBitComp()
        {
            var flags = TestFlag.B | TestFlag.D;
            var a1 = (flags & TestFlag.A) == TestFlag.A;
            var a2 = (flags & TestFlag.B) == TestFlag.B;
            var a3 = (flags & TestFlag.C) == TestFlag.C;
            var a4 = (flags & TestFlag.D) == TestFlag.D;

        }
        static void FuncHasFlag()
        {
            var flags = TestFlag.B | TestFlag.D;
            var a1 = flags.HasFlag(TestFlag.A);
            var a2 = flags.HasFlag(TestFlag.B);
            var a3 = flags.HasFlag(TestFlag.C);
            var a4 = flags.HasFlag(TestFlag.D);
        }

        static void Main(string[] args)
        {
            int count = 100000000;
            long a = 0, b = 0;

            var sw = Stopwatch.StartNew();
            
            //// Parallel.For版
            Parallel.For(0, 10, (x) =>
            {
                a += Meas(count, FuncBitComp);
                b += Meas(count, FuncHasFlag);
            });

            //// for版
            //for(int i = 0; i < 10; i++)
            //{
            //    a += Meas(count, FuncBitComp);
            //    b += Meas(count, FuncHasFlag);
            //}

            Console.WriteLine("{0}ms", sw.ElapsedMilliseconds);

            Console.WriteLine("BitComp={0}", a / 10);
            Console.WriteLine("HasFlag={0}", b / 10);

            Console.ReadLine();
        }
    }
}