C# 函数数组与开关的性能

C# 函数数组与开关的性能,c#,switch-statement,function-pointers,C#,Switch Statement,Function Pointers,在阅读了本主题之后,我编写了一个小测试来测量开关/案例样式编码与函数数组之间的性能差异。函数调用(F类成员)只使用cpu容量(算术工具):没有系统调用,没有控制台输出等I/O 最后,对于切换方法,这两种方法之间的差异大约快30%!好的,函数指针比switch/case慢一点 所以我的问题是:我的测试对你来说有效吗?还是我引入了导致这些难以置信的结果的偏见?30% ! using System; using System.Collections.Generic; using System.Linq

在阅读了本主题之后,我编写了一个小测试来测量开关/案例样式编码与函数数组之间的性能差异。函数调用(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);
//交换机中使用的枚举:

        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();
在win7 64位上编译,VS2010,Xeon E5-2609@2.4 Ghz 编译器标志:视觉标准模式发布,打开“优化代码”标志

结果:

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

跳出来的唯一一件事是,您正在为每个
maxi
迭代
new
ing您的函子列表。如果你把它移出你的循环,你的计时是什么样子的?

你的实验有一个偏差,即你定期练习开关的所有条目一次

本实验中的切换效率主要取决于“分支预测”,即我是快速命中匹配的“案例”,还是需要探索几乎所有的案例

对于这个小规模(16),我们平均得到8个布尔测试,以找到正确的“案例”,并且我们从更多的内联优化可能性中获益,因为代码结构是静态的;我认为编译器无法检测到“函子”是静态的

因此,我将尝试使函子成为在任何函数之外定义和初始化的静态常量数组(普通函子[]),以帮助编译器至少具有优化/内联的选项

然后您可以尝试增加案例的数量,在某个点上,我怀疑数组中的随机访问O(1)应该比交换机案例中的测试(总体O(Nbcases))更快


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

为什么不简单地使用OOP创建一个包含所有所需因子和一些
Calculate()
函数的类?您正在为非常示意性的行为创建10000行案例,并在高度面向对象的范例语言中使用它,
new
在pair
start/stop
方法之外,因此应该不会产生任何影响。无论如何,我会尝试一下。你会惊讶地发现,像这样的微小变化会对你所做的事情产生全面的影响。
                {

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


            }

        }

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