我正在将现有应用程序移植到 C# 并希望尽可能提高性能。许多现有的循环计数器和数组引用被定义为 System.UInt32,而不是我会使用的 Int32。
使用 UInt32 和 Int32 有什么显着的性能差异吗?
最佳答案
简短的回答是“不。任何性能影响都可以忽略不计”。
正确答案是“视情况而定”。
一个更好的问题是,“当我确定不需要符号时,我应该使用 uint 吗?”
您不能就性能给出明确的"is"或“否”的原因是因为目标平台将最终决定性能。也就是说,性能取决于将要执行代码的处理器和可用的指令。您的 .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;
}
}
两种测试方法,测试签名 和 TestUnsigned ,每个分别对有符号和无符号整数执行约 200 万次简单增量迭代。测试代码对每个测试运行 100 次迭代,并对结果求平均值。这应该消除任何潜在的不一致。我为 x64 编译的 i7-5960X 上的结果是:
Signed: 00:00:00.5066966
Unsigned: 00:00:00.5052279
这些结果几乎相同,但要得到明确的答案,我们确实需要查看为程序生成的字节码。我们可以使用 ILDASM作为 .NET SDK 的一部分来检查编译器生成的程序集中的代码。
在这里,我们可以看到 C# 编译器偏爱有符号整数,并且实际上在本地执行大多数操作作为有符号整数,并且在比较分支(也称为跳转或 if)时只将内存中的值视为无符号。尽管我们对 中的迭代器和累加器都使用了一个无符号整数。 TestUnsigned ,代码几乎与 相同测试签名 除单个指令外的方法: IL_0016 .快速浏览 ECMA spec描述差异:
blt.un.s : Branch to target if less than (unsigned or unordered), short form.
blt.s : Branch to target if less than, short form.
作为如此常见的指令,可以安全地假设大多数现代高功率处理器都有用于这两种操作的硬件指令,并且它们很可能会在相同数量的周期内执行,但是 这不能保证 .低功耗处理器可能有更少的指令,并且没有 unsigned int 的分支。在这种情况下,JIT 编译器可能必须发出多个硬件指令(例如,先转换,然后是分支)才能执行 blt.un.s IL 指令。即使是这种情况,这些附加说明也是基本的,可能不会显着影响性能。
因此,在性能方面,长答案是“使用有符号或无符号整数之间不太可能存在性能差异。如果存在差异,则可能可以忽略不计。”
那么如果性能相同,下一个合乎逻辑的问题是,“当我确定不需要符号时,我应该使用无符号值吗?”
这里有两件事需要考虑:第一,无符号整数不是 CLS-compliant ,这意味着如果您将无符号整数作为另一个程序将使用的 API 的一部分公开(例如,如果您分发可重用的库),则可能会遇到问题。其次,.NET 中的大多数操作,包括 BCL 公开的方法签名(出于上述原因),都使用有符号整数。因此,如果您打算实际使用您的无符号整数,您可能会发现自己进行了很多转换。这会对性能造成很小的影响,并且会使您的代码更加困惑。最后,这可能不值得。
TLDR; 回到我的 C++ 时代,我会说“使用任何最合适的东西,让编译器将其余部分整理出来。” C# 并不是一成不变的,所以我会为 .NET 这么说:x86/x64 上的有符号整数和无符号整数之间确实没有性能差异,但是大多数操作都需要有符号整数,所以除非你真的需要将值限制为仅正数,或者您确实需要符号位占用的额外范围,请坚持使用有符号整数。你的代码最终会更干净。
关于c# - 在 C# 中使用 UInt32 和 Int32 有什么显着的性能差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/306602/