布尔比较的效率?在C中

发布于 2024-09-26 18:08:47 字数 432 浏览 1 评论 0原文

我正在用 C 编写一个循环,我只是想知道如何对其进行优化。这并不重要,因为我只是在练习,但为了进一步了解,我想知道:

在循环中,例如以下代码片段:

int i = 0;
while (i < 10) {
    printf("%d\n", i);
    i++;
}

处理器是否检查两个 (i < 10)(i == 10) 每次迭代?或者它只是检查(i < 10),如果为真,则继续?

如果它同时检查两者,不是

int i = 0;
while (i != 10) {
    printf("%d\n", i);
    i++;
}

会更有效率吗?

谢谢!

I'm writing a loop in C, and I am just wondering on how to optimize it a bit. It's not crucial here as I'm just practicing, but for further knowledge, I'd like to know:

In a loop, for example the following snippet:

int i = 0;
while (i < 10) {
    printf("%d\n", i);
    i++;
}

Does the processor check both (i < 10) and (i == 10) for every iteration? Or does it just check (i < 10) and, if it's true, continue?

If it checks both, wouldn't:

int i = 0;
while (i != 10) {
    printf("%d\n", i);
    i++;
}

be more efficient?

Thanks!

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

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

发布评论

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

评论(9

幸福不弃 2024-10-03 18:08:47

两者都将被翻译成一条汇编指令。大多数 CPU 都有 LESS THAN、LESS THAN OR EQUAL、EQUAL 和 NOT EQUAL 比较指令。

Both will be translated in a single assembly instruction. Most CPUs have comparison instructions for LESS THAN, for LESS THAN OR EQUAL, for EQUAL and for NOT EQUAL.

栖竹 2024-10-03 18:08:47

这些优化问题的有趣之处在于,它们通常会说明为什么您应该在担心这些操作对性能的影响之前为了清晰/正确性而进行编码(哦,通常没有任何区别)。

您的 2 个示例循环没有相同的行为:

int i = 0;
/* this will print 11 lines (0..10) */
while (i <= 10) {
    printf("%d\n", i);
    i++;
}

而且,

int i = 0;
/* This will print 10 lines (0..9) */
while (i != 10) {
    printf("%d\n", i);
    i++;
}

为了回答您的问题,几乎可以肯定这两个构造的性能是相同的(假设您解决了问题,因此循环计数相同)。例如,如果您的处理器只能在两个单独的步骤中检查相等性以及一个值是否小于另一个值(这将是一个非常不寻常的处理器),那么编译器可能会转换 (i <= 10)(i < 11) 测试 - 或者可能是 (i != 11) 测试。

One of the interesting things about these optimization questions is that they often show why you should code for clarity/correctness before worrying about the performance impact of these operations (which oh-so often don't have any difference).

Your 2 example loops do not have the same behavior:

int i = 0;
/* this will print 11 lines (0..10) */
while (i <= 10) {
    printf("%d\n", i);
    i++;
}

And,

int i = 0;
/* This will print 10 lines (0..9) */
while (i != 10) {
    printf("%d\n", i);
    i++;
}

To answer your question though, it's nearly certain that the performance of the two constructs would be identical (assuming that you fixed the problem so the loop counts were the same). For example, if your processor could only check for equality and whether one value were less than another in two separate steps (which would be a very unusual processor), then the compiler would likely transform the (i <= 10) to an (i < 11) test - or maybe an (i != 11) test.

醉城メ夜风 2024-10-03 18:08:47

这是早期优化的一个明显例子……恕我直言,这是刚接触其技术的程序员很容易担心的事情。如果您必须担心它,请学习对您的代码进行基准测试和分析,以便您的担忧基于证据而不是假设。

谈谈你的具体问题。首先,在我遇到的任何 C 编译器中, <= 都没有实现为分别测试 <== 的两个操作我的职业生涯。其中包括一些极其愚蠢的编译器。请注意,对于整数,a <= 5a <= 5 的条件相同。 6,如果目标体系结构要求仅使用 <,则代码生成器将执行此操作。

您的第二个担忧是,while (i != 10) 可能会更有效,这引发了一个有趣的防御性编程问题。首先,不,在任何合理的目标架构中它都没有效率更高。然而,它增加了小错误导致更大故障的可能性。考虑一下:如果循环体内的某行代码修改了 i(例如使其大于 10),可能会发生什么?循环需要多长时间才能结束,该错误是否会产生其他后果?

最后,当想知道这种事情时,通常有必要找出您正在使用的编译器实际生成的代码。大多数编译器都提供了一种机制来执行此操作。对于 GCC,了解 -S 选项,该选项将使其直接生成汇编代码而不是生成目标文件。

This a clear example of early optimization.... IMHO, that is something that programmers new to their craft are way to prone to worry about. If you must worry about it, learn to benchmark and profile your code so that your worries are based on evidence rather than supposition.

Speaking to your specific questions. First, a <= is not implemented as two operations testing for < and == separately in any C compiler I've met in my career. And that includes some monumentally stupid compilers. Notice that for integers, a <= 5 is the same condition as a < 6 and if the target architecture required that only < be used, that is what the code generator would do.

Your second concern, that while (i != 10) might be more efficient raises an interesting issue of defensive programming. First, no it isn't any more efficient in any reasonable target architecture. However, it raises a potential for a small bug to cause a larger failure. Consider this: if some line of code within the body of the loop modified i, say by making it greater than 10, what might happen? How long would it take for the loop to end, and would there be any other consequences of the error?

Finally, when wondering about this kind of thing, it often is worthwhile to find out what code the compiler you are using actually generates. Most compilers provide a mechanism to do this. For GCC, learn about the -S option which will cause it to produce the assembly code directly instead of producing an object file.

会傲 2024-10-03 18:08:47

运算符 <= 和 <是汇编中的单个指令,应该没有性能差异。
请注意,在某些处理器上测试 0 可能比测试任何其他常量要快一些,因此使循环向后运行是合理的:

int i = 10;
while (i != 0) 
{
    printf("%d\n", i);
    i--;
}

请注意,像这样的微优化通常只能为您带来很少的性能提升,更好的性能利用您的时间来使用高效的算法。

The operators <= and < are a single instruction in assembly, there should be no performance difference.
Note that tests for 0 can be a bit faster on some processors than to test for any other constant, therefore it can be reasonable to make a loop run backward:

int i = 10;
while (i != 0) 
{
    printf("%d\n", i);
    i--;
}

Note that micro optimizations like these usually can gain you only very little more performance, better use your time to use efficient algorithms.

原谅我要高飞 2024-10-03 18:08:47

<块引用>

处理器是否在每次迭代时都检查 (i < 10) 和 (i == 10)?或者它只是检查 (i < 10),如果为真,则继续?

两者都不是,它很可能会检查(i < 11)。 <= 10 只是为了让您的代码具有更好的含义,因为 11魔法数字,实际上意味着(10+1)

Does the processor check both (i < 10) and (i == 10) for every iteration? Or does it just check (i < 10) and, if it's true, continue?

Neither, it will most likely check (i < 11). The <= 10 is just there for you to give better meaning to your code since 11 is a magic number which actually means (10+1).

对岸观火 2024-10-03 18:08:47

取决于体系结构和编译器。在大多数体系结构上,有一条指令用于 <= 或相反的指令,该指令可以被否定,因此如果将其转换为循环,则比较很可能只有一条指令。 (在 x86 或 x86_64 上它是一条指令)

编译器可能会将循环展开为十次 i++ 的序列,当仅涉及常量表达式时,它甚至会优化 ++ > 离开并只留下常数。

Ira 是对的,如果涉及 printf,那么比较就会消失,其执行时间可能是数百万个时钟周期。

Depends on the architecture and compiler. On most architectures, there is a single instruction for <= or the opposite, which can be negated, so if it is translated into a loop, the comparison will most likely be only one instruction. (On x86 or x86_64 it is one instruction)

The compiler might unroll the loop into a sequence of ten times i++, when only constant expressions are involved it will even optimize the ++ away and leave only constants.

And Ira is right, the comparison does vanish if there is a printf involved, which execution time might be millions of clock cycles.

温柔戏命师 2024-10-03 18:08:47

我正在用 C 语言编写一个循环,我只是想知道如何对其进行优化。

如果您在打开优化的情况下进行编译,则最大的优化将来自展开该循环。

使用 -O2 分析该代码将很困难,因为对于简单的函数,编译器将展开循环,并且您将无法对比较中的实际差异进行基准测试。在分析使用常量的测试用例时应该小心,这些常量在编译器优化时可能会使代码变得琐碎。

I'm writing a loop in C, and I am just wondering on how to optimize it a bit.

If you compile with optimizations turned on, the biggest optimization will be from unrolling that loop.

It's going to be hard to profile that code with -O2, because for trivial functions the compiler will unroll the loop and you won't be able to benchmark actual differences in compares. You should be careful when profiling test cases that use constants that might make the code trivial when optimized by the compiler.

此刻的回忆 2024-10-03 18:08:47

拆卸。根据处理器、优化和许多事情,这个简单的示例代码实际上展开或执行的操作并不反映您的真正问题。尽管您提供的两个示例循环都使用 gcc -O1 进行编译,但生成了相同的汇编程序(对于arm)。

如果大于或等于循环的远端,C 代码中的小于通常会变成分支。如果您的处理器没有大于或等于,则它可能有大于或等于分支,如果大于或等于则分支,这两条指令。

通常,虽然会有一个寄存器保存 i。将有一条指令来增加 i。然后将 i 与 10 进行比较、然后等于、大于或等于和小于的指令通常在一条指令中完成,因此您通常不会看到差异。

disassemble. Depending on the processor, and optimization and a number of things this simple example code actually unrolls or does things that do not reflect your real question. Compiling with gcc -O1 though both example loops you provided resulted in the same assembler (for arm).

Less than in your C code often turns into a branch if greater than or equal to the far side of the loop. If your processor doesnt have a greater than or equal it may have a branch if greater than and a branch if equal, two instructions.

typically though there will be a register holding i. there will be an instruction to increment i. Then an instruction to compare i with 10, then equal to, greater than or equal, and less than are generally done in a single instruction so you should not normally see a difference.

笑饮青盏花 2024-10-03 18:08:47
// Case I

int i = 0; 
while (i < 10) {
    printf("%d\n", i);
    i++;
    printf("%d\n", i);
    i++;
}

// Case II

int i = 0;
while (i < 10) {
    printf("%d\n", i);
    i++;
}

与案例 I 代码相比,案例 I 代码占用更多空间但速度更快,案例 II 代码占用更少空间但速度较慢。
因为在编程中空间复杂度和时间复杂度总是成正比的。这意味着你必须妥协空间或时间。
因此,通过这种方式,您可以优化时间复杂度或空间复杂度,但不能同时优化两者。

而且你的两个代码是相同的。

// Case I

int i = 0; 
while (i < 10) {
    printf("%d\n", i);
    i++;
    printf("%d\n", i);
    i++;
}

// Case II

int i = 0;
while (i < 10) {
    printf("%d\n", i);
    i++;
}

Case I code take more space but fast and Case II code is take less space but slow compare to Case I code.
Because in programming space complexity and time complexity always proportional to each other. It means you must compromise either space or time.
So in that way you can optimize your time complexity or space complexity but not both.

And your both code are same.

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