在背靠背for循环中的int,short,byte性能

gin*_*boy 14 c# performance types for-loop

(背景:为什么我应该在C#中使用int而不是字节或short)

为了满足我自己对使用"适当大小"整数与"优化"整数的优缺点的好奇心,我编写了以下代码,强化了我以前在.Net中对int性能的真实性(并在链接中对此进行了解释)以上)它是针对int性能而不是short或byte进行优化的.

DateTime t;
long a, b, c;

t = DateTime.Now;
for (int index = 0; index < 127; index++)
{
    Console.WriteLine(index.ToString());
}           
a = DateTime.Now.Ticks - t.Ticks;

t = DateTime.Now;
for (short index = 0; index < 127; index++)
{
    Console.WriteLine(index.ToString());
}

b=DateTime.Now.Ticks - t.Ticks;

t = DateTime.Now;           
for (byte index = 0; index < 127; index++)
{
    Console.WriteLine(index.ToString());
}
c=DateTime.Now.Ticks - t.Ticks;

Console.WriteLine(a.ToString());
Console.WriteLine(b.ToString());
Console.WriteLine(c.ToString());
Run Code Online (Sandbox Code Playgroud)

这在......的范围内给出了大致一致的结果.

〜95万

〜2000000

〜1700000

这与我期望看到的一致.

但是当我尝试重复每个数据类型的循环时......

t = DateTime.Now;
for (int index = 0; index < 127; index++)
{
    Console.WriteLine(index.ToString());
}
for (int index = 0; index < 127; index++)
{
    Console.WriteLine(index.ToString());
}
for (int index = 0; index < 127; index++)
{
    Console.WriteLine(index.ToString());
}
a = DateTime.Now.Ticks - t.Ticks;
Run Code Online (Sandbox Code Playgroud)

数字更像......

〜450

〜3100000

〜30万

我发现这令人费解.任何人都可以提供解释吗?

注意:为了比较,我喜欢将循环限制为127,因为字节值类型的范围.这也是一种好奇心而非生产代码微优化的行为.

Aar*_*ght 41

首先,它不是针对int性能进行优化的.NET,它是优化的机器,因为32位是本机字大小(除非您使用的是x64,在这种情况下它是long64位).

其次,你在每个循环中写入控制台 - 这比增加和测试循环计数器要昂贵得多,所以你不会在这里测量任何真实的东西.

第三,a的byte范围最大为255,所以你可以循环254次(如果你试图做255,它会溢出而循环永远不会结束 - 但你不需要停在128).

第四,你没有做任何接近足够的迭代来进行分析.迭代128或甚至254次的紧密循环是没有意义的.你应该做的是将byte/ short/ int循环放在另一个迭代次数很多的循环中,比如1000万,并检查结果.

最后,DateTime.Now在计算中使用会在分析时产生一些定时"噪声".建议(并且更容易)使用秒表类.

最重要的是,在进行有效的性能测试之前,这需要进行许多更改.


以下是我认为更准确的测试程序:

class Program
{
    const int TestIterations = 5000000;

    static void Main(string[] args)
    {
        RunTest("Byte Loop", TestByteLoop, TestIterations);
        RunTest("Short Loop", TestShortLoop, TestIterations);
        RunTest("Int Loop", TestIntLoop, TestIterations);
        Console.ReadLine();
    }

    static void RunTest(string testName, Action action, int iterations)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();
        Console.WriteLine("{0}: Elapsed Time = {1}", testName, sw.Elapsed);
    }

    static void TestByteLoop()
    {
        int x = 0;
        for (byte b = 0; b < 255; b++)
            ++x;
    }

    static void TestShortLoop()
    {
        int x = 0;
        for (short s = 0; s < 255; s++)
            ++x;
    }

    static void TestIntLoop()
    {
        int x = 0;
        for (int i = 0; i < 255; i++)
            ++x;
    }
}
Run Code Online (Sandbox Code Playgroud)

这会在一个更大的循环(500万次迭代)中运行每个循环,并在循环内执行非常简单的操作(递增变量).我的结果是:

字节循环:经过时间= 00:00:03.8949910
短循环:经过时间= 00:00:03.9098782
Int循环:经过时间= 00:00:03.2986990

所以,没有明显的区别.

此外,确保您在发布模式下进行配置,许多人忘记并在调试模式下进行测试,这将显着降低准确性.

  • 哦,谢谢,我以前从未真正尝试过分析我的代码。好点,在船上采取:) (2认同)
  • @Aaronaught:我喜欢我们的基准测试有多相似:) (2认同)
  • @Jon:我发誓我没有复制你的.:P (2认同)
  • 哦,我根本没想到.只是觉得好笑. (2认同)

Jon*_*eet 12

大部分时间可能都花在了写入控制台上.尝试在循环中做一些不同的事情......

另外:

  • 使用DateTime.Now是一种衡量时间的坏方法.请System.Diagnostics.Stopwatch改用
  • 一旦你摆脱了这个Console.WriteLine调用,一个127次迭代的循环就太短了,无法衡量.你需要运行循环大量的时间来获得一个合理的测量.

这是我的基准:

using System;
using System.Diagnostics;

public static class Test
{    
    const int Iterations = 100000;

    static void Main(string[] args)
    {
        Measure(ByteLoop);
        Measure(ShortLoop);
        Measure(IntLoop);
        Measure(BackToBack);
        Measure(DelegateOverhead);
    }

    static void Measure(Action action)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            action();
        }
        sw.Stop();
        Console.WriteLine("{0}: {1}ms", action.Method.Name,
                          sw.ElapsedMilliseconds);
    }

    static void ByteLoop()
    {
        for (byte index = 0; index < 127; index++)
        {
            index.ToString();
        }
    }

    static void ShortLoop()
    {
        for (short index = 0; index < 127; index++)
        {
            index.ToString();
        }
    }

    static void IntLoop()
    {
        for (int index = 0; index < 127; index++)
        {
            index.ToString();
        }
    }

    static void BackToBack()
    {
        for (byte index = 0; index < 127; index++)
        {
            index.ToString();
        }
        for (short index = 0; index < 127; index++)
        {
            index.ToString();
        }
        for (int index = 0; index < 127; index++)
        {
            index.ToString();
        }
    }

    static void DelegateOverhead()
    {
        // Nothing. Let's see how much
        // overhead there is just for calling
        // this repeatedly...
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

ByteLoop: 6585ms
ShortLoop: 6342ms
IntLoop: 6404ms
BackToBack: 19757ms
DelegateOverhead: 1ms
Run Code Online (Sandbox Code Playgroud)

(这是在上网本上 - 调整迭代次数,直到你得到合理的东西:)

这似乎表明它与您使用的类型基本没有显着差异.

  • @runrunraygun:`Console.WriteLine`是一个异步操作,执行时间不可靠.虽然它不太可能会对您的结果产生巨大影响,但使用更可靠的东西.另外,`int.ToString()`与`byte.ToString()`的功能不同,所以你不会在每个循环中执行相同的操作. (5认同)

小智 5

出于好奇,我从Aaronaught修改了一个程序,并以x86和x64模式进行了编译。奇怪,Int在x64中的运行速度快得多:

x86

字节循环:经过的时间= 00:00:00.8636454
短循环:经过的时间= 00:00:00.8795518
UShort循环:经过的时间= 00:00:00.8630357
Int循环:经过的时间= 00:00:00.5184154
UInt循环:经过的时间= 00:00:00.4950156
长循环:经过的时间= 00:00:01.2941183超长
循环:经过的时间= 00:00:01.3023409

x64

字节循环:经过的时间= 00:00:01.0646588
短循环:经过的时间= 00:00:01.0719330
UShort循环:经过的时间= 00:00:01.0711545
Int循环:经过的时间= 00:00:00.2462848
UInt循环:经过的时间= 00:00:00.4708777
长循环:经过的时间= 00:00:00.5242272超长
循环:经过的时间= 00:00:00.5144035