什么C ++代码编译成x86 REP指令?

时间:2011-01-27 05:36:24

标签: c++ arrays assembly machine-instruction

我在C ++中将元素从一个数组复制到另一个数组。我发现x86中的rep movs指令似乎将ESI处的数组复制到EDI大小为ECX的数组。但是,我尝试的forwhile循环都没有编译成VS 2008中的rep movs指令(在Intel Xeon x64处理器上)。如何编写将编译为该指令的代码?

6 个答案:

答案 0 :(得分:11)

老实说,你不应该这样做。 REP在指令集中是一种过时的保留,实际上非常慢,因为它必须调用CPU内部的微编码子程序,它具有ROM查找延迟并且也是非流水线。

在几乎每个实现中,您都会发现memcpy()编译器内在函数更易于使用且运行速度更快。

答案 1 :(得分:5)

在MSVC下,有__movsxxx& __stosxxx内在函数将生成REP前缀指令。

由于crt中的sse2分支,还有一个'hack'强制在vc9 +下强制内在memset又名REP STOS,因为内在不再存在。这比__stosxxx更好,因为编译器可以为常量优化它并正确排序。

#define memset(mem,fill,size) memset((DWORD*)mem,((fill) << 24|(fill) << 16|(fill) << 8|(fill)),size)
__forceinline void memset(DWORD* pStart, unsigned long dwFill, size_t nSize)
{
    //credits to Nepharius for finding this
    DWORD* pLast = pStart + (nSize >> 2);
    while(pStart < pLast)
        *pStart++ = dwFill;

    if((nSize &= 3) == 0)
        return;

    if(nSize == 3)
    {
        (((WORD*)pStart))[0]   = WORD(dwFill);
        (((BYTE*)pStart))[2]   = BYTE(dwFill);
    }
    else if(nSize == 2)
        (((WORD*)pStart))[0]   = WORD(dwFill);
    else
        (((BYTE*)pStart))[0]   = BYTE(dwFill);
}

当然REP并不总是最好的选择,使用memcpy更好的方式,它会根据您的系统分支到sse2或REPS MOV(在msvc下,除非你想为“热门”区域编写自定义程序集......

答案 2 :(得分:3)

如果您需要完全相同的指令 - 请使用内置汇编程序并手动编写该指令。 You can't rely on the compiler to produce any specific machine code - 即使它在一次编译中发出它,它也可以决定在下一次编译时发出一些其他的等价物。

答案 3 :(得分:1)

当x86 CPU是单管道工业CISC处理器时,REP和朋友很高兴。

但这已经改变了。现在,当处理器遇到任何指令时,它首先将其转换为更简单的格式(类似VLIW的微操作)并安排它以供将来执行(这是无序的一部分) - 执行,不同逻辑CPU内核之间的调度的一部分,它可以用于将写后写序列简化为单写,等等。这种机制适用于转换为几个类似VLIW的操作码的指令,但不适用于转换为循环的机器代码。循环转换的机器代码可能会导致执行管道停止。

不是花费成千上万的晶体管来构建CPU电路来处理执行流水线中微操作的循环部分,而是在某种蹩脚的遗留模式中处理它,这种模式会使管道断断续续,并要求现代程序员编写自己该死的循环!

因此,当机器编写代码时很少使用它。如果你在一个二进制可执行文件中遇到REP,它可能是一个不知道更好的人类汇编 - muppet,或者是一个真正需要它保存的几个字节来使用它而不是实际循环的破解者。 / p>

(但是。把我刚写的所有东西都拿出来。也许这不再是真的了。我不再100%更新x86 CPU的内部了,我进入了其他的爱好......)

答案 4 :(得分:0)

我使用带有cmps *,movs *,scas *和stos *指令变体的rep *前缀变体来生成内联代码,从而最大限度地减少代码大小,避免不必要的调用/跳转,从而减少缓存所做的工作。另一种方法是设置参数并在其他地方调用memset或memcpy,如果我想复制一百个字节或更多,可能总体上更快,但如果使用rep只需要10-20个字节就更快(或者至少是我最后一次测量了。)

由于我的编译器允许内联汇编函数的规范和使用,并在优化活动中包含它们的寄存器使用/修改,因此我可以在情况合适时使用它们。

答案 5 :(得分:0)

历史性的一点 - 没有深入了解制造商的策略 - 曾经有一段时间“rep movs *”(等)指令非常缓慢。我认为这是奔腾/奔腾MMX的时代。我的一位同事(他比我更有洞察力)说,制造商已经减少了分配给rep处理的芯片面积(减少了更少的晶体管/更多的微代码),并使用它来更快地制造其他更多使用的指令。

在十五年左右的时间里,rep再次变得更快,这表明更多的晶体管/更少的微码。