代码之家  ›  专栏  ›  技术社区  ›  Noah

在c中,使用uint32和int32有没有显著的性能差异?

  •  11
  • Noah  · 技术社区  · 17 年前

    我正在将一个现有的应用程序移植到C,并希望尽可能提高性能。许多现有的循环计数器和数组引用被定义为System.UInt32,而不是我应该使用的Int32。

    使用uint32和int32有什么显著的性能差异吗?

    7 回复  |  直到 10 年前
        1
  •  10
  •   Scott Dorman    17 年前

    除了处理器级别的有符号算术和无符号算术之间可能存在的差异之外,我认为没有任何性能方面的考虑,但是在这一点上,我认为这些差异是没有意义的。

    更大的区别在于CLS遵从性,因为无符号类型不符合CLS,因为并非所有语言都支持它们。

        2
  •  19
  •   Robear    10 年前

    简短的回答是“不,任何性能影响都可以忽略不计”。

    正确答案是“视情况而定”。

    一个更好的问题是,“当我确定不需要符号时,我应该使用uint吗?”

    您不能就性能给出确定的“是”或“否”是因为目标平台最终将决定性能。也就是说,性能取决于执行代码的处理器和可用的指令。您的.NET代码编译为 Intermediate Language (IL或字节码)。然后,这些指令由 Just-In-Time (JIT)编译器作为 Common Language Runtime (CLR)。您不能控制或预测将为每个用户生成什么代码。

    既然知道硬件是性能的最终仲裁者,问题就变成了:“有符号整数和无符号整数的代码.NET生成有什么不同?”以及“差异是否会影响我的应用程序和目标平台?”

    回答这些问题的最佳方法是进行测试。

    class Program
    {
      static void Main(string[] args)
      {
        const int iterations = 100;
        Console.WriteLine($"Signed:      {Iterate(TestSigned, iterations)}");
        Console.WriteLine($"Unsigned:    {Iterate(TestUnsigned, iterations)}");
        Console.Read();
      }
    
      private static void TestUnsigned()
      {
        uint accumulator = 0;
        var max = (uint)Int32.MaxValue;
        for (uint i = 0; i < max; i++) ++accumulator;
      }
    
      static void TestSigned()
      {
        int accumulator = 0;
        var max = Int32.MaxValue;
        for (int i = 0; i < max; i++) ++accumulator;
      }
    
      static TimeSpan Iterate(Action action, int count)
      {
        var elapsed = TimeSpan.Zero;
        for (int i = 0; i < count; i++)
          elapsed += Time(action);
        return new TimeSpan(elapsed.Ticks / count);
      }
    
      static TimeSpan Time(Action action)
      {
        var sw = new Stopwatch();
        sw.Start();
        action();
        sw.Stop();
        return sw.Elapsed;
      }
    }
    

    两种测试方法, 测试签名 未签署的 ,分别对有符号整数和无符号整数执行大约200万次简单增量迭代。测试代码对每个测试运行100次迭代,并对结果进行平均。这将消除任何潜在的不一致。我为x64编译的i7-5960x的结果是:

    Signed:      00:00:00.5066966
    
    Unsigned:    00:00:00.5052279
    

    这些结果几乎是相同的,但是为了得到一个明确的答案,我们真的需要看看为程序生成的字节码。我们可以使用 ILDASM 作为.NET SDK的一部分,检查编译器生成的程序集中的代码。

    Bytecode

    在这里,我们可以看到C编译器倾向于使用有符号整数,并且实际上以有符号整数本地执行大多数操作,并且在比较分支(例如跳转或if)时,只将内存中的值视为无符号。尽管事实上我们正在为中的迭代器和累加器使用无符号整数 未签署的 ,代码与 测试签名 除单个指令外的方法: ILY-0.16 . 快速浏览 ECMA spec 描述区别:

    联合国: 小于(无符号或无序)时分支到目标,短格式。

    BLT:S: 如果小于,则分支到目标,缩写。

    作为一种常见的指令,我们可以安全地假设大多数现代大功率处理器都有两种操作的硬件指令,它们很可能在相同的周期内执行,但是 这不能保证 .低功耗处理器的指令可能更少,并且没有无符号int的分支。在这种情况下,JIT编译器可能必须发出多个硬件指令(例如,先转换,然后是分支)才能执行 联合国 IL指令。即使是这样,这些附加指令也是基本的,可能不会对性能产生显著影响。

    因此,在性能方面,长期的答案是“使用有符号整数和无符号整数之间不太可能存在性能差异”。如果存在差异,则可能忽略不计。”

    因此,如果性能相同,下一个逻辑问题是,“当我确定不使用无符号值时,是否应该使用无符号值?” 需要 一个牌子?”

    这里有两件事要考虑:第一,无符号整数不是 CLS-compliant ,也就是说,如果您将无符号整数作为另一个程序将使用的API的一部分公开(例如,如果您正在分发可重用库),则可能会遇到问题。其次,.NET中的大多数操作,包括BCL公开的方法签名(基于上面的原因),都使用有符号整数。因此,如果计划实际使用无符号整数,您可能会发现自己对它进行了大量的转换。这将有一个非常小的性能影响,并将使您的代码有点混乱。最后,这可能不值得。

    TLDR; 回到我的C++时代,我会说:“使用最合适的方法,让编译器把其余的排序出来。”C.*不太像割和干,所以我会这样说.NET:x86/x64上的有符号和无符号整数之间实际上没有性能上的差异,但是大多数操作都需要带符号整数,所以除非你真的需要限制VALU。只对正数,或者您确实需要符号位所吃的额外范围,请使用带符号整数。你的代码最终会更干净。

        3
  •  3
  •   James Curran    17 年前

    我在.NET中没有对这个问题做任何研究,但是在Win32/C++的旧时代,如果你想把一个“已签名的int”转换成一个“已签名的long”,CPU必须运行一个OP来扩展符号。要将“unsigned int”强制转换为“unsigned long”,它的上部字节中只有零。节省是按照几个时钟周期的顺序进行的(也就是说,你必须做数十亿次才能有一个甚至可以感知的差异)

        4
  •  3
  •   Alan    17 年前

    就性能而言,没有区别。简单的整数计算是众所周知的,现代CPU高度优化以快速执行它们。

        5
  •  1
  •   JohnFx    17 年前

    这些类型的优化很少值得这样做。使用最适合任务的数据类型,并将其保留。如果这件事太多接触到数据库,你可能会在数据库设计、查询语法或索引策略中找到十几个调整,这些调整会将C语言中的代码优化抵消几百个数量级。

        6
  •  0
  •   GWLlosa    17 年前

    它将以任何一种方式分配相同数量的内存(尽管它可以存储更大的值,因为它不为符号节省空间)。所以我怀疑你会看到“性能”的不同,除非你使用大值/负值,这会导致一个选项或另一个选项爆炸。

        7
  •  0
  •   Rohan West    17 年前

    这与性能无关,而与循环计数器的要求无关。

    prehaps有很多迭代要完成

            Console.WriteLine(Int32.MaxValue);      // Max interation 2147483647
            Console.WriteLine(UInt32.MaxValue);     // Max interation 4294967295
    

    无符号int可能是有原因的。