if(var == true)是否比if(var!= false)更快?

时间:2010-09-29 05:23:49

标签: c# optimization if-statement conditional boolean

非常简单的问题。我知道它可能只是一个很小的优化,但最终你会使用足够的if语句来解决它。

编辑:感谢那些提供答案的人。

对于那些觉得有必要打击我的人,要知道好奇心和对知识的渴望不会转化为愚蠢。

非常感谢所有提出建设性批评的人。直到现在我才知道if(var)的能力。我漂亮确定我现在会使用它。 ;)

10 个答案:

答案 0 :(得分:68)

首先:回答性能问题的唯一方法是衡量。亲自尝试,你会发现。

至于编译器的作用:我提醒你“if”只是一个条件goto。当你有

if (x)
   Y();
else
   Z();
Q();

编译器将其生成为:

evaluate x
branch to LABEL1 if result was false
call Y
branch to LABEL2
LABEL1:
call Z
LABEL2:
call Q

evaluate !x
branch to LABEL1 if result was true

取决于是否更容易生成代码以引出“x”恰好是“正常”或“反转”结果。例如,如果您有if (a<=b),则可能更容易将其生成为(if !(a>b))。或相反亦然;这取决于正在编译的确切代码的细节。

无论如何,我怀疑你有更大的鱼要炸。如果您关心性能,使用分析器找到最慢的东西,然后修复。当你可能在你的程序中的其他地方浪费整整毫秒时,毫无意义地担心纳秒优化。

答案 1 :(得分:42)

您是否知道在x86处理器上执行x ^= x(其中x是32位整数)比执行x = 0更有效?这是真的,当然也有相同的结果。因此,只要有人在代码中看到x = 0,就可以将其替换为x ^= x并提高效率。

现在,您是否见过很多代码中的x ^= x

你没有的原因不仅仅是因为效率增益很小,而是因为这正是编译器(如果编译为本机代码)或抖动(如果编译IL或类似代码)会产生的那种变化。反汇编一些x86代码并且看到程序集等效于x ^= x并不罕见,尽管编译为执行此操作的代码几乎肯定有x = 0或者可能更复杂的代码如x = 4 >> 6x = 32 - y对代码的分析显示y此时将始终包含32,依此类推。

出于这个原因,即使已知x ^= x更有效,但在绝大多数情况下,它的唯一效果会使代码更不可读(唯一的例外是在使用的算法中需要进行x ^= y,而我们碰巧做的是xy在这里相同的情况,在这种情况下{{1将x ^= x隐藏它时,该算法的使用会更清晰。

在99.999999%的案例中,同样适用于您的示例。在剩余的0.000001%的情况下,它应该存在效率差异,但是在某些奇怪的运算符覆盖之间,编译器无法将其解析为另一个。事实上,0.000001%是夸大案件,刚才提到因为我很确定如果我努力了,我可以写一些效率低于另一个的东西。通常人们都不会努力这样做。

如果您在反射器中查看自己的代码,您可能会发现一些与您编写的代码看起来非常不同的情况。这样做的原因在于它是对代码的IL进行逆向工程,而不是对代码本身进行逆向工程,实际上,您经常会发现x = 0if(var == true)之类的东西被转换为{{{ 1}}或甚至转换为if(var != false)if(var)if(!var)块被颠倒。

看得更深,你会发现即使是进一步的改变,也有不止一种方法可以让同一只猫皮肤光滑。特别是,查看if语句转换为IL的方式很有意思;有时候它变成了一堆else语句的等价物,有时它变成了一个可以进行跳转的表的查找,这取决于在所讨论的情况下看起来更有效。

更深入地了解并在编译为本机代码时进行其他更改。

我不会同意那些谈论“过早优化”的人只是因为你询问两种不同方法之间的性能差异,因为对这种差异的了解是一件好事,它只是过早地利用了这些知识。过早(根据定义)。但是,即将被编译出来的变化既不是过早的,也不是优化,它只是一个零变化。

答案 2 :(得分:18)

根本没有任何区别。使用反射器,您可以看到代码:

private static void testVar(bool var)
{
    if (var == true)
    {
        Console.WriteLine("test");
    }

    if (var != false)
    {
        Console.WriteLine("test");
    }

    if (var)
    {
        Console.WriteLine("test");
    }
}

创建IL:

.method private hidebysig static void testVar(bool var) cil managed
{
  .maxstack 8
  L_0000: ldarg.0 
  L_0001: brfalse.s L_000d
  L_0003: ldstr "test"
  L_0008: call void [mscorlib]System.Console::WriteLine(string)
  L_000d: ldarg.0 
  L_000e: brfalse.s L_001a
  L_0010: ldstr "test"
  L_0015: call void [mscorlib]System.Console::WriteLine(string)
  L_001a: ldarg.0 
  L_001b: brfalse.s L_0027
  L_001d: ldstr "test"
  L_0022: call void [mscorlib]System.Console::WriteLine(string)
  L_0027: ret 
}

因此编译器(在.Net 3.5中)将它们全部转换为ldarg.0,brfalse.s指令集。

答案 3 :(得分:11)

无论您在程序中使用多少次迭代,都不会产生任何可衡量的差异。

(改为使用if (var);您不需要比较的视觉混乱。)

答案 4 :(得分:11)

它将绝对零差异,因为编译器几乎肯定会将两个语句编译为相同的二进制代码。

(伪)程序集将是:

test reg1, reg2
br.true somewhere
; code for false case

somewhere:
; code for true case

test reg1, reg2
br.false somewhere
; code for true case

somewhere:
; code for false case

编译器选择哪一个不取决于您是写== true还是!= false。相反,它是编译器根据真假案例代码的大小以及其他一些因素进行的优化。

顺便说一句,Linux内核代码实际上尝试使用LIKELYUNLIKELY宏来优化这些分支,因为它的if条件,所以我猜它可以手动控制它。

答案 5 :(得分:8)

通常有效的经验法则是“如果你知道他们做同样的事情,那么编译器也会知道”。

如果编译器知道这两种形式会产生相同的结果,那么它将选择最快的

因此,假设它们同样快,直到你的探查者告诉你。

答案 6 :(得分:5)

始终优化以便于理解。就我而言,这是编程的基本规则。你不应该微观优化,甚至优化 知道你需要这样做,以及你需要这样做的地方。这是一个非常罕见的情况,挤出每一盎司的性能比可维护性更重要,甚至更罕见的是你知道在最初编写代码时优化的位置。

此外,这样的事情会以任何不错的语言自动优化。

tl;博士不要打扰

答案 7 :(得分:4)

其他答案都很好,我只是想补充一下:

这不是一个有意义的问题,因为它假设符号与生成的IL或本机代码之间存在1:1的关系。

没有。即使在C ++中也是如此,甚至在C中也是如此。你必须一直到本机代码才能让这样的问题有意义。

已编辑添加:

第一个Fortran编译器(约1957年)的开发人员有一天在审查其输出时感到惊讶。它发出的代码显然不正确(虽然它是);实质上,它正在做出不明显正确的优化决策(尽管它们是)。

这个故事的寓意:50多年来,编译器比人们更聪明。除非您准备检查其输出和/或进行广泛的性能测试,否则不要试图超越它们。

答案 8 :(得分:2)

没有区别,编译器可以随意交换它们。

例如,你可以写

if (!predicate)
    statement1;
else
    statement2;

并且编译器可以自由发出相当于

的代码
if (predicate)
    statement2;
else
    statement1;

反之亦然。

答案 9 :(得分:1)

了解这两个特定情况中的哪一个更快是一个细节级别,在高级语言中很少(如果有的话)。如果你的编译器在优化时很糟糕,也许你可能需要知道它。但是,如果你的编译器那么糟糕,如果可能的话,你可能会更好地获得更好的编译器。

如果你是在装配编程,你更有可能知道这两种情况会更好。其他人已经就分支声明给出了程序集细分,所以我不打算复制响应的那一部分。但是,我认为遗漏的一个项目是比较项目。

可以想象处理器可以在加载'var'时改变状态标志。如果是,则如果'var'为0,则可以在将变量加载到寄存器中时设置零标志。使用这样的处理器,不需要与FALSE进行明确的比较。等效的汇编伪代码将是......

load 'var' into register
branch if zero or if not zero as appropriate

使用这个相同的处理器,如果你要对它进行测试,那么程序集伪代码就是......

load 'var' into register
compare that register to TRUE (a specific value)
branch if equal or if not equal as appropriate

在实践中,任何处理器的行为都是这样的吗?我不知道 - 其他人比我更了解情况。我知道有些人不会以这种方式行事,但我不知道所有人。

假设某些处理器的行为与上述场景相同,我们可以学到什么? IF(这是一个很大的IF)你会担心这一点,避免测试布尔值与显式值...

if (var == TRUE)
if (var != FALSE)

并使用以下其中一项来测试布尔类型...

if (var)
if (!var)