代码之家  ›  专栏  ›  技术社区  ›  John Alexiou

与结构尺寸和性能有关的特殊结果

  •  6
  • John Alexiou  · 技术社区  · 14 年前

    我对使用操作符时大结构和小结构的开销很好奇 + * Small 有1个双字段(8字节)和1个 Big 有10个双字节(80字节)。在我所有的操作中,我只操作一个叫做 x .

    首先我定义了两种结构的数学运算符,比如

    public static Small operator +(Small a, Small b)
    {
        return new Small(a.x + b.x);
    }
    public static Small operator *(double x, Small a)
    {
        return new Small(x * a.x);
    }
    

    这会消耗堆栈中大量的内存来复制周围的字段。我对一个数学运算进行了5000000次迭代,得到了我所怀疑的结果(慢了3倍)。

    public double TestSmall()
    {
        pt.Start(); // pt = performance timing object
        Small r = new Small(rnd.NextDouble()); //rnd = Random number generator
        for (int i = 0; i < N; i++)
        {
            a = 0.6 * a + 0.4 * r;   // a is a local field of type Small
        }
        pt.Stop();
        return pt.ElapsedSeconds;
    }
    

    发布代码的结果(秒)

    Small=0.33940 Big=0.98909     Big is Slower by x2.91
    

    ref 论据

    public static void Add(ref Small a, ref Small b, ref Small res)
    {
        res.x = a.x + b.x;
    }
    public static void Scale(double x, ref Small a, ref Small res)
    {
        res.x = x * a.x;
    }
    

    并在此测试代码上运行相同次数的迭代:

    public double TestSmall2()
    {
        pt.Start(); // pt = performance timing object
        Small a1 = new Small(); // local
        Small a2 = new Small(); // local
        Small r = new Small(rnd.NextDouble()); //rdn = Random number generator
        for (int i = 0; i < N; i++)
        {
            Small.Scale(0.6, ref a, ref a1);
            Small.Scale(0.4, ref r, ref a2);
            Small.Add(ref a1, ref a2, ref a);
        }
        pt.Stop();
        return pt.ElapsedSeconds;
    }
    

    Small=0.11765 Big=0.07130     Big is Slower by x0.61
    

    因此,与mem copy-intensive操作符相比,我得到了x3和x14的加速,这是非常好的,但是比较小结构时间和大结构时间,你会看到小结构时间是60% 更慢的

    有人能解释一下吗?它是否与CPU管道有关,在(空间上)内存中分离操作有助于更有效地预取数据?

    http://dl.dropbox.com/u/11487099/SmallBigCompare.zip

    5 回复  |  直到 14 年前
        1
  •  1
  •   Jon Skeet    14 年前

    我无法复制你的结果。在我的框中,“ref”版本的性能基本上与 Big Small ,在公差范围内。

    (在没有附加调试器的情况下运行Release模式,迭代次数是原来的10到100倍,只是为了获得一个很好的长时间运行。)

    你试过运行你的版本很多次吗?有没有可能在测试运行时,您的CPU正在逐渐提高其时钟速度(因为它发现它必须努力工作)?

        2
  •  3
  •   JaredPar    14 年前

    1. Stopwatch 而不是 PerformanceTimer EllapsedSeconds 而不是 EllapsedMilliseconds .
    2. 每个测试应该运行两次,只计算第二次,以消除潜在的JIT成本
    3. Marshal.SizeOf 不是生成结构的实际大小,只是它的编组大小。

    切换到后 秒表 我看到基准测试在静态ref情况下为这两种类型生成几乎相等的时间,从而达到预期的效果。

        3
  •  0
  •   Brian Gideon    14 年前

    • 使用 Stopwatch 班级。它使用完全相同的Win32 API,但是已经为您编写了代码。
    • 增加迭代次数,使您的基准测试至少需要1秒(或更多)才能运行,否则异常情况可能会突然出现并支配时间。
    • 考虑到vshost.exe过程。根据您是独立运行应用程序还是通过visualstudio主机进程运行应用程序,调试版本和发布版本都会得到不同的结果。

    真正让我印象深刻的是,在一个独立的发布版本中,这个较小的结构要快得多

    独立发布版本:

    Size of Small is 8 bytes
    Size of Big is 80 bytes
    50,000,000.00 Iterations
    Operator Results
      Small=0.57173 Big=25.58988    Slower=x44.76
    
    StaticRef Results
      Small=26.06602        Big=26.68569    Slower=x1.02
      Small=x0.02   Big=x0.96
    

    Size of Small is 8 bytes
    Size of Big is 80 bytes
    50,000,000.00 Iterations
    Operator Results
      Small=4.56601 Big=35.33387    Slower=x7.74
    
    StaticRef Results
      Small=37.94317        Big=39.64959    Slower=x1.04
      Small=x0.12   Big=x0.89
    
        4
  •  0
  •   csharptest.net    14 年前

    同意Jared的观点,这是一个基准测试错误。

        Size of Small is 8 bytes
        Size of Big is 80 bytes
    
        5,000,000.00 Iterations
        Operator Results
          Small=523.00000       Big=1953.00000  Slower=x3.73
        StaticRef Results
          Small=2042.00000      Big=2125.00000  Slower=x1.04
          Small=x0.26   Big=x0.92
    
        5,000,000.00 Iterations
        Operator Results
          Small=2464.00000      Big=3510.00000  Slower=x1.42
        StaticRef Results
          Small=3578.00000      Big=3647.00000  Slower=x1.02
          Small=x0.69   Big=x0.96
    
        5,000,000.00 Iterations
        Operator Results
          Small=3921.00000      Big=4817.00000  Slower=x1.23
        StaticRef Results
          Small=4880.00000      Big=4944.00000  Slower=x1.01
          Small=x0.80   Big=x0.97
    
        5
  •  0
  •   John Alexiou    14 年前

    谢谢大家的意见。下面是一些最后的想法。

    最终结果:

    1
    2
    . 对于运算符,大结构比小结构慢20倍(如预期的那样)
    4

    最后一个问题是,由于不应进行堆栈复制,使用by ref使大结构变慢的机制是什么?


    Size of Small is 8 bytes
    Size of Big is 80 bytes
    5,000,000.00 Iterations
    Warming up the CPU's
    
    Using QueryPerformanceCounter
    Operator Results
      Small=0.03545 Big=0.71519     Slower=x20.18
    
    StaticRef Results
      Small=0.03526 Big=0.05194     Slower=x1.47
      Small=x1.01   Big=x13.77