功能与开关阵列的性能

时间:2014-05-28 13:31:08

标签: c# switch-statement function-pointers

在阅读了这个主题:Performance of array of functions over if and switch statementshttp://en.wikipedia.org/wiki/Branch_table之后,我写了一个小测试来衡量一个开关/案例样式编码与一系列函数之间的性能差异。函数调用(F类成员)仅故意使用cpu容量(算术内容):没有系统调用,没有像控制台输出那样的I / O等。

最后,这两种方法之间的差异对于切换方法来说快了大约30%!好的,函数指针比switch / case慢一点。

所以我的问题是:我的测试看起来对您有效吗?或者我是否引入了任何导致这些令人难以置信的结果的偏见? 30%!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {

//使用的仿函数:

        delegate void functor(int i, int j);

//在switch中使用的枚举:

        enum indexes
        {
            a = 0, b = 1, c = 2, d = 3, e = 4, f = 5,
            g = 6, h = 7, i = 8, j = 9, k = 10,
            l = 11, m = 12, n = 13, o = 14, p = 15,
            q = 16
        };

//具有不同可能调用的类:

        class F
        {
            long m_j = 1;
            public void A(int i, int j) { m_j = (i + j - 2) % (j / 3 + 1); }
            public void B(int i, int j) { m_j = (i + j - 3) % (j / 3 + 1); }
            public void C(int i, int j) { m_j = (i + j - 4) % (j / 3 + 1); }
            public void D(int i, int j) { m_j = (i + j - 5) % (j / 3 + 1); }
            public void E(int i, int j) { m_j = (i + j - 6) % (j / 3 + 1); }
            public void FF(int i, int j) { m_j = (i + j - 7) % (j / 3 + 1); }
            public void G(int i, int j) { m_j = (i + j - 8) % (j / 3 + 1); }
            public void H(int i, int j) { m_j = (i + j - 9) % (j / 3 + 1); }
            public void I(int i, int j) { m_j = (i + j - 10) % (j / 3 + 1); }
            public void J(int i, int j) { m_j = (i + j - 11) % (j / 3 + 1); }
            public void K(int i, int j) { m_j = (i + j - 12) % (j / 3 + 1); }
            public void L(int i, int j) { m_j = (i + j - 13) % (j / 3 + 1); }
            public void M(int i, int j) { m_j = (i + j - 14) % (j / 3 + 1); }
            public void N(int i, int j) { m_j = (i + j - 15) % (j / 3 + 1); }
            public void O(int i, int j) { m_j = (i + j - 16) % (j / 3 + 1); }
            public void P(int i, int j) { m_j = (i + j - 17) % (j / 3 + 1); }
            public void Q(int i, int j) { m_j = (i + j - 18) % (j / 3 + 1); }
            public static int nbfunc = 17;
        }


        static void Main(string[] args)
        {

//在每一轮,我们增加了通话次数:

            long maxi = 1000;
            for (; maxi < 10000000000; maxi *= 10)
            {
                long switch_time, array_time;
                TextWriter tw = Console.Out;
                {
                    Stopwatch sw = new Stopwatch();
                    F f = new F();

// ***************使用开关/案例进行测试***************

                    sw.Start();
                    for (int i = 0; i < maxi; i++)
                    {
                        indexes e = (indexes)(i % F.nbfunc);
                        switch (e)
                        {
                            case indexes.a:
                                f.A(i,i/2);
                                break;
                            case indexes.b:
                                f.B(i, i / 2);
                                break;
                            case indexes.c:
                                f.C(i, i / 2);
                                break;
                            case indexes.d:
                                f.D(i, i / 2);
                                break;
                            case indexes.e:
                                f.E(i, i / 2);
                                break;
                            case indexes.f:
                                f.FF(i, i / 2);
                                break;
                            case indexes.g:
                                f.G(i, i / 2);
                                break;
                            case indexes.h:
                                f.H(i, i / 2);
                                break;
                            case indexes.i:
                                f.I(i, i / 2);
                                break;
                            case indexes.j:
                                f.J(i, i / 2);
                                break;
                            case indexes.k:
                                f.K(i, i / 2);
                                break;
                            case indexes.l:
                                f.L(i, i / 2);
                                break;
                            case indexes.m:
                                f.M(i, i / 2);
                                break;
                            case indexes.n:
                                f.N(i, i / 2);
                                break;
                            case indexes.o:
                                f.O(i, i / 2);
                                break;
                            case indexes.p:
                                f.P(i, i / 2);
                                break;
                        }
                    }
                    sw.Stop();
                    switch_time = sw.ElapsedMilliseconds;
                }
                //

// ***************使用funcs数组进行测试***************

                {

                    Stopwatch sw = new Stopwatch();
                    F f = new F();

                    List<functor> functors = new List<functor>()
                    {
                        f.A, f.B, f.C, f.D, f.E, f.FF, f.G, f.H, f.I, f.J, f.K, f.L, f.M, f.N, f.O, f.P, f.Q 
                    };

                    sw.Start();
                    for (int i = 0; i < maxi; i++)
                    {
                        functors[i % F.nbfunc](i, i / 2);
                    }
                    sw.Stop();
                    array_time = sw.ElapsedMilliseconds;
                }

// ***************显示结果***************

                Console.WriteLine("nb iterations : " + maxi.ToString());
                Console.WriteLine("  switch method total time in ms : " + (switch_time).ToString());
                Console.WriteLine("  array  method total time in ms : " + (array_time).ToString());


            }

        }

    }
}

在win7 64bits,VS2010,Xeon E5-2609 @ 2.4 Ghz上编译 编译器标志:可视标准模式发布,使用&#34;优化代码&#34;国旗。

结果:

nb iterations : 1000000
  switch method total time in ms : 19
  array  method total time in ms : 23
nb iterations : 10000000
  switch method total time in ms : 177
  array  method total time in ms : 237
nb iterations : 100000000
  switch method total time in ms : 1808
  array  method total time in ms : 2416
nb iterations : 1000000000
  switch method total time in ms : 18630
  array  method total time in ms : 24312

2 个答案:

答案 0 :(得分:1)

唯一突然出现的是,newmaxi次迭代{{1}}你的仿函数列表。如果你把它移到你的循环之外,你的时间是什么样的?

答案 1 :(得分:1)

您的实验存在偏差,即您定期对开关执行所有条目。

此实验中的切换效率由&#34;分支预测&#34;支配,即我是否匹配&#34; case&#34;我还需要快速探索所有病例吗?

对于这个小尺寸(16),我们平均得到平均8次布尔测试以找到正确的&#34; case&#34;,并且我们从更多的内联优化可能性中获益,因为代码结构是静态的;我不认为编译器可以检测到&#34;仿函数&#34;是静态的。

因此,我会尝试将仿函数定义为在任何函数外部定义和初始化的静态常量数组(简单仿函数[]),以帮助编译器至少具有优化/内联选项。

然后你可以尝试增加案例的数量,在某些时候我怀疑数组中的随机访问O(1)应该比switch案例中的测试更快(整体O(Nbcases))。

最后,尝试绘制一些随机访问模式,(为两次运行重放相同的种子以避免随机偏差),因为交换机版本可能会受影响但不会影响阵列版本。