【问题标题】:Loop without comparison n times in C在C中循环不比较n次
【发布时间】:2015-03-02 17:39:24
【问题描述】:

如果我有一个我知道需要执行 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();
}

【问题讨论】:

  • 这不是你应该做的。如果更有效,您的编译器已经展开循环。
  • 您为什么要这样做?它会在指令缓存中占用更多空间,需要更多指令解码,而且基本上会更糟。如果它更好,编译器不会为你转换它吗? (除非它是一块垃圾,在这种情况下你为什么要使用它?)

标签: c++ c loops


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    大多数编译器更智能,在您的第二个示例中可以生成如下代码:

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

    自动,因为他们检测到循环总是会迭代 5 次。

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

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

    与幼稚版本相比,这将使counts 的测试量增加约五分之一。

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

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

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

    而不是

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

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

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

    do { ... } while 版本的机器代码只是

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

    【讨论】:

    • 您可以使用以下命令展开最后一个循环:switch(count) {case 4:operation(); case 3: operation(); case 2: operation(); case 1: operation(); }
    【解决方案3】:

    两个循环都会进行比较..

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

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

        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>());
        }
        

        Live demo

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

        【讨论】:

          猜你喜欢
          • 2013-09-06
          • 1970-01-01
          • 2016-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-04
          • 1970-01-01
          • 2012-01-07
          相关资源
          最近更新 更多