这种循环的替代方式可以更有效吗?

时间:2011-03-30 23:52:10

标签: c++ optimization assembly

一个下雨的下午我很无聊,想出了这个:

int ia_array[5][5][5]; //interger array called array

{
        int i = 0, j = 0, k = 0;//counters
        while( i < 5 )//loop conditions
        {
            ia_array[i][j][k] = 0;//do something
            __asm inc k;//++k;

            if( k > 4)
            {
                __asm inc j;  //++j;
                __asm mov k,0;///k = 0;
            }
            if( j > 4)
            {
                __asm inc i;  //++i;
                __asm mov j,0;//j = 0;
            }
        }//end of while
    }//i,j,k fall out of scope

它在功能上等同于三个嵌套for循环。但是在for循环中,您不能使用__asm语句。您还可以选择不将计数器放在作用域中,以便可以将它们重用于其他循环。我已经查看了两者的反汇编,我的替代品有15个操作码,嵌套的for循环有24个。因此它可能更快吗?假设我真的在问__asm inc i;比++ i更快?

注意:出于好奇,我不打算在任何项目中使用此代码。谢谢你的时间。

5 个答案:

答案 0 :(得分:1)

不,它甚至不会更快。事实上,它可能很容易变慢。你的编译器的优化器几乎肯定比你更有效。

答案 1 :(得分:1)

这将是非常特定的编译器和编译器开关,但是每个循环迭代你的代码将有三个测试,其中传统的嵌套循环每个循环迭代只有一个,所以我认为你的方法往往是一般来说慢一点。

答案 2 :(得分:1)

首先,您的编译器可能会将i,j和k的值存储在寄存器中。

执行for (i = 4; i <=0; i--)for(i = 0; i < 5; i++)更有效率,因为cpu可以确定它执行的最后一次操作的结果是否为零 - 它不必显式比较为4(参见cmovz指令)。

x86的情况并非如此,必须执行较少的指令将导致更快的代码。与指令流水线有关的各种问题很快就会让程序员手动编写。把它留给编译器,它们现在足够高效(虽然绝对不是最佳的...但是谁想等待数小时才能编译代码)。

您可以通过每次执行运行几十万次功能来检查自己,并检查哪个更快。检查是否可以使用

在for循环中编写asm指令
__asm {
    inc j;
    mov k, 0;
}

(自从我这么做以来已经有一段时间了)

P.S。尝试使用asm很有趣,它可以非常有趣和有益!

答案 3 :(得分:1)

有几件事:

  1. 您无法根据输出中的操作码数来判断汇编代码的速度。编译器可以展开循环来消除分支,许多现代编译器将尝试像上面那样对循环进行矢量化。前者可以拥有比天真代码更多的操作码并且速度更快,后者可以更少,更快。

  2. 通过在代码中添加__asm语句,您可能排除编译器可以对循环执行的任何优化。因此,如果您使用类似于英特尔编译器的快速编译,那么您的代码可能会比编译器的性能更差。对于像这里的代码一样简单的事情尤其如此,其中数组大小是静态已知的,并且循环边界是恒定的。

  3. 如果您真的想了解编译器能做什么/不能做什么,可以去读一本书或者学习优化编译器和矢量化的课程。有许多不同的优化,并且理解即使是在特定架构上这样的简单代码的性能也可能是微妙的。

    有很多内核和数字运算代码,编译器仍然无法比 knowledgable 人类做得更好,但是如果没有很多架构细节方面的经验,那么你不会做得比icc -fastxlC -O5

答案 4 :(得分:1)

虽然 肯定可以在优化时击败编译器,但你 不会这样做。你用汇编语言编写的这些位非常明显,机械类型的翻译可以很容易地完成任何中档合适的编译器(甚至非常糟糕的编译器)。

如果你想击败编译器,你需要更进一步,例如重新安排指令以允许更多并行执行(绝对不重要)或找到比编译器更好的指令序列。

在这种情况下,例如,你可能至少有机会注意到iarray[5][5][5]可以(从汇编语言的角度来看)被视为5 * 5 * 5 = 125个元素的单个扁平数组,并将大部分基本上都是memset编码到一条指令中:

mov ecx, 125    // 125 elements
xor eax, eax    // set them to zero
mov di, offset ia_array // where we're going to store them
rep stosd       // and fill that memory.

然而,实际上,这可能不会是编译器可能产生的主要(或可能甚至是次要的)改进。它更有可能接近(至少接近)跟上所需的最低限度。

下一步是考虑使用非临时存储而不是简单的stosd。这实际上不会加速这个循环(无论如何),但是如果缓存中已经存在的其他代码可能立即更加重要,则可以通过避免此存储污染缓存来获得一定的速度。你也可以使用其他一些SSE指令来获得一点速度 - 但即使最多,你也不能指望比这几个百分点好多少。最重要的是,为了使一些内存归零,速度主要受到总线速度的限制,而不是你使用的指令,因此你所做的任何事情都不会有太大帮助。