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

发布于 2024-09-25 11:54:40 字数 195 浏览 1 评论 0原文

很简单的问题。我知道这可能是一个微小的优化,但最终您将使用足够的 if 语句来使其发挥作用。

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

对于那些觉得有必要抨击我的人,要知道好奇心和对知识的渴望并不意味着愚蠢。

非常感谢所有提出建设性批评的人。直到现在我还不知道如何声明 if(var) 。我相当确定我现在就会使用它。 ;)

Pretty simple question. I know it would probably be a tiny optimization, but eventually you'll use enough if statements for it to matter.

EDIT: Thank you to those of you who have provided answers.

To those of you who feel a need to bash me, know that curiosity and a thirst for knowledge do not translate to stupidity.

And many thanks to all of those who provided constructive criticism. I had no knowledge of the ability to state if(var) until now. I'm pretty sure I'll be using it now. ;)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

初见终念 2024-10-02 11:54:40

首先:回答性能问题的唯一方法是测量它。亲自尝试一下,您就会知道。

至于编译器的作用:我提醒你,“if”只是一个条件跳转。当您让

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)) 可能会更容易。反之亦然;这取决于正在编译的确切代码的细节。

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

First off: the only way to answer performance question is to measure it. Try it yourself and you'll find out.

As for what the compiler does: I remind you that "if" is just a conditional goto. When you have

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

the compiler generates that as either:

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

or

evaluate !x
branch to LABEL1 if result was true

depending on whether it is easier to generate the code to elicit the "normal" or "inverted" result for whatever "x" happens to be. For example, if you have if (a<=b) it might be easier to generate it as (if !(a>b)). Or vice versa; it depends on the details of the exact code being compiled.

Regardless, I suspect you have bigger fish to fry. If you care about performance, use a profiler and find the slowest thing and then fix that. It makes no sense whatsoever to be worried about nanosecond optimizations when you probably are wasting entire milliseconds somewhere else in your program.

和影子一齐双人舞 2024-10-02 11:54:40

您是否知道,在 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 的情况 在这里是相同的,在这种情况下,x ^= x 将使该算法的使用更加清晰,而 x = 0 会隐藏它)。

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

如果您曾经在反射器中查看过自己的代码,您可能会发现在某些情况下它看起来与您编写的代码非常不同。这样做的原因是,它是对代码的 IL 进行逆向工程,而不是代码本身,事实上,您经常会发现的一件事是 if(var == true) 或 < code>if(var != false) 被转换为 if(var) 甚至通过 if 转换为 if(!var) code> 和 else 块颠倒了。

更深入地观察,您会发现甚至发生了进一步的变化,因为有不止一种方法可以剥同一只猫的皮。特别是,查看 switch 语句转换为 IL 的方式很有趣;有时它会变成一堆 if-else if 语句,有时它会变成对可以进行的跳转表的查找,具体取决于哪个看起来更有效有问题的案件。

更深入地研究一下,当它被编译为本机代码时,还会进行其他更改。

我不会同意那些仅仅因为你询问两种不同方法之间的性能差异而谈论“过早优化”的人,因为了解这种差异是一件好事,只有过早地使用这些知识才是不成熟的(通过定义)。但是,将被编译掉的更改既不是不成熟的,也不是优化,它只是一个无效的更改。

Did you know that on x86 processors it's more efficient to do x ^= x where x is a 32-bit integer, than it is to do x = 0? It's true, and of course has the same result. Hence any time one can see x = 0 in code, one can replace it with x ^= x and gain efficiency.

Now, have you ever seen x ^= x in much code?

The reason you haven't is not just because the efficiency gain is slight, but because this is precisely the sort of change that a compiler (if compiling to native code) or jitter (if compiling IL or similar) will make. Disassemble some x86 code and its not unusual to see the assembly equivalent of x ^= x, though the code that was compiled to do this almost certainly had x = 0 or perhaps something much more complicated like x = 4 >> 6 or x = 32 - y where analysis of the code shows that y will always contain 32 at this point, and so on.

For this reason, even though x ^= x is known to be more efficient, the sole effect of it in the vast, vast majority of cases would be to make the code less readable (the only exception would be where doing x ^= y was entailed in an algorithm being used and we happened to be doing a case where x and y were the same here, in this case x ^= x would make the use of that algorithm clearer while x = 0 would hide it).

In 99.999999% percent of cases the same is going to apply to your example. In the remaining 0.000001% cases it should but there's an efficiency difference between some strange sort of operator overrides and the compiler can't resolve one to the other. Indeed, 0.000001% is overstating the case, and is just mentioned because I'm pretty sure that if I tried hard enough I could write something where one was less efficient than the other. Normally people aren't trying hard to do so.

If you ever look at your own code in reflector, you'll probably find a few cases where it looks very different to the code you wrote. The reason for this is that it is reverse-engineering the IL of your code, rather than your code itself, and indeed one thing you will often find is things like if(var == true) or if(var != false) being turned into if(var) or even into if(!var) with the if and else blocks reversed.

Look deeper and you'll see that even further changes are done in that there is more than one way to skin the same cat. In particular, looking at the way switch statements gets converted to IL is interesting; sometimes it gets turned into the equivalent of a bunch of if-else if statements, and sometimes it gets turned into a lookup into a table of jumps that could be made, depending on which seemed more efficient in the case in question.

Look deeper still and other changes are made when it gets compiled to native code.

I'm not going to agree with those who talk of "premature optimisation" just because you ask about the performance difference between two different approaches, because knowledge of such differences is a good thing, it's only using that knowledge prematurely that is premature (by definition). But a change that is going to be compiled away is neither premature, nor an optimisation, its just a null change.

旧竹 2024-10-02 11:54:40

这根本没有什么区别。使用 Reflector,您可以看到代码:

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 指令集。

It will make no difference at all. Using reflector you can see that the code:

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

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

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

creates the 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 
}

So the compiler (in .Net 3.5) translates them all to the ldarg.0, brfalse.s instruction set.

仲春光 2024-10-02 11:54:40

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

(伪)程序集将是:

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 条件,所以我猜想可以手动控制它。

It will make absolutely zero difference, because the compiler would almost certainly compile the two statements to the same binary code.

The (pseudo) assembly will either be:

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

somewhere:
; code for true case

or

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

somewhere:
; code for false case

Which of those the compiler chooses will not depend on whether you write == true or != false. Rather, it's an optimisation the compiler will make based on the size of the true and false case code and perhaps some other factors.

As an aside, the Linux kernel code actually does try to optimise these branches using LIKELY and UNLIKELY macros for its if conditions, so I guess it is possible to manually control it.

爱你是孤单的心事 2024-10-02 11:54:40

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

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

Makes no measurable difference at all, no matter how many iterations you use in your program.

(Use if (var) instead; you don't need the visual clutter of the comparisons.)

山有枢 2024-10-02 11:54:40

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

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

因此,假设它们同样快,直到你的分析器告诉你否则。

A rule of thumb that usually works is "If you know they do the same thing, then the compiler knows too".

If the compiler knows that the two forms yield the same result, then it will pick the fastest one.

Hence, assume that they are equally fast, until your profiler tells you otherwise.

紅太極 2024-10-02 11:54:40

始终进行优化以易于理解。就我而言,这是编程的基本规则。您不应该进行微优化,甚至不应该完全优化,除非您知道您需要这样做以及在哪里需要这样做。在极少数情况下,充分发挥性能比可维护性更重要,甚至更罕见的是,您非常出色,以至于在最初编写代码时就知道在哪里进行优化。

此外,任何像样的语言都会自动优化类似的事情。

tl;博士别打扰

Always optimize for ease of understanding. This is a cardinal rule of programming, as far as I am concerned. You should not micro-optimize, or even optimize at all until you know that you need to do so, and where you need to do so. It's a very rare case that squeezing every ounce of performance out is more important than maintainability and it's even rarer that you're so awesome that you know where to optimize as you initially write the code.

Furthermore, things like this get automatically optimized out in any decent language.

tl;dr don't bother

舞袖。长 2024-10-02 11:54:40

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

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

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

编辑添加:

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

这个故事的寓意是:50 多年来,编译器一直比人类聪明。除非您准备好检查他们的输出和/或进行广泛的性能测试,否则不要试图智胜他们。

The other answers are all good, I just wanted to add:

This is not a meaningful question, because it assumes a 1:1 relation between the notation and the resulting IL or native code.

There isn't. And that's true even in C++, and even in C. You have to go all the way down to native code to have such a question make sense.

Edited to add:

The developers of the first Fortran compiler (ca. 1957) were surprised one day when reviewing its output. It was emitting code that was not obviously correct (though it was); in essence, it was making optimization decisions that were not obviously correct (though they were).

The moral of this story: compilers have been smarter than people for over 50 years. Don't try to outsmart them unless you're prepared to examine their output and/or do extensive performance testing.

忘你却要生生世世 2024-10-02 11:54:40

这没有什么区别,编译器可以随意互换它们。

例如,您可以编写

if (!predicate)
    statement1;
else
    statement2;

并且编译器可以自由地发出等效的代码

if (predicate)
    statement2;
else
    statement1;

,反之亦然。

It makes no difference, and the compiler is free to interchange them at will.

For example, you could write

if (!predicate)
    statement1;
else
    statement2;

and the compiler is free to emit code equivalent to

if (predicate)
    statement2;
else
    statement1;

or vice-versa.

不即不离 2024-10-02 11:54:40

知道这两种特定情况中哪一种更快是高级语言中很少(如果有的话)需要的详细程度。如果您的编译器在优化方面非常糟糕,也许您可​​能需要知道这一点。然而,如果你的编译器那么糟糕,如果可能的话,总体来说你可能会得到更好的编译器。

如果您正在使用汇编语言进行编程,那么您对这两种情况的了解可能会更好。其他人已经给出了有关分支语句的程序集细分,因此我不会费心重复响应的该部分。然而,我认为有一项被省略了,那就是比较。

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

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

使用相同的处理器,如果您要对其进行 TRUE 测试,则汇编伪代码将是...

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

在实践中,是否有任何处理器具有这样的行为?我不知道——其他人会比我更有知识。我确实知道有些人不这样做,但我不知道全部。

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

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

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

if (var)
if (!var)

Knowing which of these two specific cases is faster is a level of detail that is seldom (if ever) required in a high-level language. Perhaps you might require to know it if your compiler is piss poor at optimizations. However, if your compiler is that bad, you would probably be better off overall in getting a better one if possible.

If you are programming in assembly, it is more likely that you knowledge of the two cases would be better. Others have already given the assembly breakdown with respect to branch statements, so I will not bother to duplicate that part of the response. However, one item that has been omitted in my opinion is that of the comparison.

It is conceivable that a processor may change the status flags upon loading 'var'. If so, then if 'var' is 0, then the zero-flag may be set when the variable is loaded into a register. With such a processor, no explicit comparison against FALSE would be required. The equivalent assembly pseudo-code would be ...

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

Using this same processor, if you were to test it against TRUE, the assembly pseudo-code would be ...

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

In practice, do any processors behave like this? I don't know--others will be more knowledgeable than I. I do know of some that don't behave in this fashion, but I do not know about all.

Assuming that some processors do behave as in the scenario described above, what can we learn? IF (and that is a big IF) you are going to worry about this, avoid testing booleans against explicit values ...

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

and use one of the following for testing boolean types ...

if (var)
if (!var)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文