在 C 中循环而不进行比较 n 次

Loop without comparison n times in C

本文关键字:比较 循环      更新时间:2023-10-16

如果我有一个我知道需要执行n次的循环,有没有办法编写一个while(或for)循环而不每次进行比较?如果没有,有没有办法进行宏转换:

int i = 0;
for(i = 0; i < 5; i++) {
   operation();
}

到:

operation();
operation();
operation();
operation();
operation();

附言这是我迄今为止想出的最快的循环。

int i = 5;
while(i-- >= 0) {
   operation();
}

一个足够聪明的编译器会为你做到这一点。 更具体地说,优化编译器了解循环展开。 这是一个相当基本的优化,尤其是在像您的示例这样在编译时知道迭代次数的情况下。

简而言之:打开编译器优化,不要担心。

您在源代码中编写的指令数量与编译器将生成的机器指令数量没有严格关系。

大多数编译器都更聪明,在第二个示例中可以生成如下代码:

operation();
operation();
operation();
operation();
operation();

自动,因为它们检测到循环将始终迭代 5 次。

此外,如果您执行面向分析的优化,并且编译器发现循环具有很小的主体和非常高的重复计数,则即使对于具有以下代码的通用迭代次数,它也可能会展开它:

while (count >= 5) {
    operation();
    operation();
    operation();
    operation();
    operation();
    count -= 5;
}
while (count > 0) {
    operation();
    count--;
}

与朴素版本相比,这将使大count大约五分之一的测试。

这是否值得做是只有分析才能判断的事情。

如果您确定代码至少需要执行一次,您可以做的一件事是编写

do {
    operation();
} while (--count);

而不是

while (count--) {
    operation();
}

对于CPU来说,count==0的可能性有些烦人,因为在大多数编译器生成的代码中都需要额外的JMP转发:

    jmp test
loop:
    ...operation...
test:
    ...do the test...
    jne loop

相反,do { ... } while版本的机器代码很简单

loop:
    ... opertion ...
    ... do the test...
    jne loop

两个循环都会进行比较。

无论如何,编译器应该识别常量迭代并展开循环。

您可以使用 gcc 和优化标志 (-O) 进行检查,然后查看生成的代码。

更重要的是:除非有重要理由,否则不要优化!

编译完 C 代码后,while 和 for 循环将转换为机器语言中的比较语句,因此无法避免与 for/while 循环进行某种类型的比较。您可以制作一系列避免使用比较的 goto 和算术语句,但结果可能会效率较低。您应该研究如何使用 radare2 或 gdb 将这些循环编译为机器语言,看看如何改进它们。

使用模板,您可以使用以下内容展开循环(在编译时已知计数):

namespace detail
{
    template <std::size_t ... Is>
    void do_operation(std::index_sequence<Is...>)
    {
        std::initializer_list<std::size_t>{(static_cast<void>(operation()), Is)...};
    }
}
template <std::size_t N>
void do_operation()
{
    detail::do_operation(std::make_index_sequence<N>());
}

现场演示

但是编译器可能已经对正常循环进行了这种优化。