诱导GCC发出REPE CMPSB

Geo*_*son 2 c performance x86 assembly gcc

如何哄骗GCC编译器在普通C中发出REPE CMPSB指令,而没有"asm"和"_emit"关键字,调用包含的库和编译器内在函数?

我尝试了一些像下面列出的C代码,但没有成功:

unsigned int repe_cmpsb(unsigned char *esi, unsigned char *edi, unsigned int ecx) {

    for (; ((*esi == *edi) && (ecx != 0)); esi++, edi++, ecx--); 

    return ecx;
}
Run Code Online (Sandbox Code Playgroud)

请参阅GCC如何在此链接上编译它:https:
//godbolt.org/g/obJbpq

PS
我意识到无法保证编译器以某种方式编译C代码,但我还是想哄它以获得乐趣,只是为了看它有多聪明.

Pet*_*des 7

rep cmps不快; 例如,Haswell的每个计数吞吐量> = 2个周期,加上启动开销.(http://agner.org/optimize).你可以得到一个常规的一次一个字节循环,每个时钟进行1次比较(现代CPU可以每个时钟运行2次加载),即使你必须检查匹配和0终结器,如果你仔细地写它.

InstLatx64号码同意:Haswell可以管理每个字节1个周期rep cmpsb,但这是总带宽(即比较每个字符串1个字节的2个周期).


只有rep movs与rep stos有"快字符串"支持在当前的x86 CPU.(即,当对齐和缺少重叠时,内部使用更宽的加载/存储的微编码实现.)

现代CPU的"智能"之处在于使用SSE2 pcmpeqb/ pmovmskb.(但是gcc和clang不知道如何使用在循环输入之前未知的迭代计数来向量化循环;即它们不能对搜索循环进行向量化.但ICC可以.)


然而,由于某种原因,内嵌GCC将repz cmpsb用于strcmp对短期固定的字符串.据推测,它不知道任何更聪明的内联模式strcmp,并且启动开销可能仍然比动态库函数的函数调用开销更好.或许不是,我还没有测试过.无论如何,代码块中的代码大小与一堆固定字符串进行比较并不可怕.

#include <string.h>

int string_equal(const char *s) {
    return 0 == strcmp(s, "test1");
}
Run Code Online (Sandbox Code Playgroud)

来自Godbolt的gcc7.3 -O3输出

.LC0:
    .string "test1"
string_cmp:
    mov     rsi, rdi
    mov     ecx, 6
    mov     edi, OFFSET FLAT:.LC0
    repz cmpsb
    setne   al
    movzx   eax, al
    ret
Run Code Online (Sandbox Code Playgroud)

如果你没有以某种方式对结果进行boolean化,gcc会使用seta/setb/sub/movzx生成-1/0/+1结果.(在IvyBridge之前导致Intel上的部分寄存器停顿,以及对其他CPU的错误依赖,因为它sub在setcc结果上使用32位,/ facepalm.幸运的是,大多数代码只需要strcmp的双向结果,而不是3-way ).

gcc只使用固定长度的字符串常量来执行此操作,否则它将不知道如何设置rcx.


结果完全不同memcmp:gcc做得很好,在这种情况下使用DWORD和WORD cmp,没有rep字符串指令.

int cmp_mem(const char *s) {
    return 0 == memcmp(s, "test1", 6);
}

    cmp     DWORD PTR [rdi], 1953719668  # 0x74736574
    je      .L8
.L5:
    mov     eax, 1
    xor     eax, 1          # missed optimization here after the memcmp pattern; should just xor eax,eax
    ret
.L8:
    xor     eax, eax
    cmp     WORD PTR [rdi+4], 49     # check last 2 bytes
    jne     .L5
    xor     eax, 1
    ret
Run Code Online (Sandbox Code Playgroud)

控制此行为

手册说-mstringop-strategy=libcall应该强制库调用,但它不起作用.asm输出没有变化.

也没有-mno-inline-stringops-dynamically -mno-inline-all-stringops.

看来这部分海湾合作委员会文件已经过时了.我没有进一步调查更大的字符串文字,或固定大小但非常量字符串,或类似.