为什么在C#项目属性>下打开“检查算术下溢/溢出”时呢?构建>高级,以下代码比关闭选项(141毫秒)运行得更快(138毫秒)?
using System;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
var s = new Stopwatch();
s.Start();
int a = 0;
for (int i = 0; i < 100000000; i += 3) {
if (i == 1000)
i *= 2;
if (i % 35 == 0)
++a;
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds);
Console.WriteLine(a);
}
}
另一方面,如果您注释掉if (i == 1000) i *= 2;
,则检查的代码比未检查的代码(116毫秒)运行得慢(120毫秒)。
using System;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
var s = new Stopwatch();
s.Start();
int a = 0;
for (int i = 0; i < 100000000; i += 3) {
if (i % 35 == 0)
++a;
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds);
Console.WriteLine(a);
}
}
进程:重复从PowerShell提示符在Visual Studio外部手动运行.exe,直到生成的时间戳保持一致(±1 ms);多次在设置之间翻转以确保一致的结果。
测试框设置:
答案 0 :(得分:7)
答案是你正在处理许多常量,这些常量允许JIT做出安全的假设,它永远不会溢出。如果您使用类似Fibbonacci基准的东西,差异就会变得清晰。
2770ms vs 4150ms(AnyCPU,32位首选)
using System;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
var s = new Stopwatch();
s.Start();
int a = 0;
for (int i = 0; i < 100000000; i++)
{
a = Fibonacci(45);
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds);
}
public static int Fibonacci(int n)
{
int a = 0;
int b = 1;
for (int i = 0; i < n; i++)
{
int temp = a;
a = b;
// if the JIT compiler is clever, only this one needs to be 'checked'
b = temp + b;
}
return a;
}
}
答案 1 :(得分:3)
在这种情况下,检查算术比未选中更快,原因有两个:
编译器能够确定检查大部分算法是不必要的,因此检查的额外开销很小。这是一个简单的测试工件。 leppie's answer给出了一个具有实质性差异的算法的一个很好的例子。
为实现检查而插入的代码恰好导致密钥分支目标不会落在对齐边界上。您可以通过两种方式看到这一点:
将int a = 0;
替换为int a = args.Length;
。运行测试并观察性能反转消失。原因是附加代码导致分支目标对齐。
检查下面的装配。我通过将Process.EnterDebugMode();
和Debugger.Break();
添加到Main
的末尾并从命令行运行Release模式.exe来获取它。注意当检查的代码对i % 35 == 0
进行测试时,如果为false,它将分支到00B700CA,这是一个对齐的指令。与未经检查的代码对比,分支到012D00C3。即使已检查的代码有一条额外的jo
指令,对齐分支的节省也会超过其成本。
int a = 0;
00B700A6 xor ebx,ebx
for (int i = 0; i < 100000000; i += 3) {
00B700A8 xor esi,esi
if (i == 1000)
00B700AA cmp esi,3E8h
00B700B0 jne 00B700B7
i *= 2;
00B700B2 mov esi,7D0h
if (i % 35 == 0)
00B700B7 mov eax,esi
00B700B9 mov ecx,23h
00B700BE cdq
00B700BF idiv eax,ecx
00B700C1 test edx,edx
00B700C3 jne 00B700CA
++a;
00B700C5 add ebx,1
00B700C8 jo 00B70128
for (int i = 0; i < 100000000; i += 3) {
00B700CA add esi,3
00B700CD jo 00B70128
00B700CF cmp esi,5F5E100h
00B700D5 jl 00B700AA
}
int a = 0;
012D00A6 xor ebx,ebx
for (int i = 0; i < 100000000; i += 3) {
012D00A8 xor esi,esi
if (i == 1000)
012D00AA cmp esi,3E8h
012D00B0 jne 012D00B4
i *= 2;
012D00B2 add esi,esi
if (i % 35 == 0)
012D00B4 mov eax,esi
012D00B6 mov ecx,23h
012D00BB cdq
012D00BC idiv eax,ecx
012D00BE test edx,edx
012D00C0 jne 012D00C3
++a;
012D00C2 inc ebx
for (int i = 0; i < 100000000; i += 3) {
012D00C3 add esi,3
012D00C6 cmp esi,5F5E100h
012D00CC jl 012D00AA
}