C增量指针与for()循环性能

时间:2018-08-03 23:52:14

标签: c performance

我一直想知道,是否在所有情况下都等同于这样写:

char* op = buf;
int l = buflen;
while( l > 0 )
{
    *op = bufval;
    op++;
    l--;
}

以这种方式:

int l;
for( l = 0; l < buflen; l++ )
{
    buf[ l ] = bufval;
}
从众多现代编译器和计算平台的性能角度来看?后者的代码当然更优雅,但这不是重点。我看到检查“ l大于0”就像是汇编程序中的简单JNZ,而“ l小于bufval”则需要比较op。 “ buf [l]”可能不需要相对于“ op ++”的附加指令,但是我不知道这在实践中如何影响性能。在某些情况下,例如“ op”需要增加3时,第一个变体是可取的,这比我想写“ l * 3”要好得多。

2 个答案:

答案 0 :(得分:2)

这是两个代码段的生成程序集

f2:                          ;; pointer based approach
        test    edx, edx
        jle     .L5
        sub     edx, 1
        movsx   esi, sil
        movsx   rdx, edx
        add     rdx, 1
        jmp     memset
.L5:
        ret

f3:                          ;; loop based approach
        test    edx, edx
        jle     .L8
        sub     edx, 1
        movsx   esi, sil
        add     rdx, 1
        jmp     memset
.L8:
        ret

我知道较短的汇编并不意味着更快的代码,但是编译器确实为基于指针的版本生成了一些额外的指令。当我使用clang尝试相同指令时,指令数量的差异甚至更大。如果有的话,基于指针的版本会稍微慢一些,而不是更快。

请注意,这两者都在调用memset,在此之前的代码仅是检查和设置用于memset调用的寄存器。您可以继续进行memset通话。

memset(buf, bufval, buflen)

这将生成以下程序集

f1:                          ;; memset based approach
        movsx   rdx, edx
        jmp     memset

回到最初的问题,哪个版本更快。不能过分强调现代编译器很聪明。像这样的微优化很少(如果有的话)提供性能优势。编写惯用代码(使编译器更容易理解其意图)将始终为您带来更好的性能。

如果您要使用程序集输出:https://godbolt.org/g/NxHS5F

,这里是指向Godbolt的链接。

答案 1 :(得分:1)

您看到的差异仅是因为您的函数是使用非常不友好且糟糕的方式以非常好的编译器优化器编写的。

如果编写得更好,两者都应编译相同:

void foo(char *buf, int bufval, size_t buflen)
{
    while(buflen--)
    {
        *buf++ = bufval;
    }
}


void foo1(char *buf, int bufval, size_t buflen)
{

    size_t l;
    for( l = 0; l < buflen; l++ )
    {
        buf[ l ] = bufval;
    }
}


foo:
        test    rdx, rdx
        je      .L11
        movsx   esi, sil
        jmp     memset
.L11:
        ret
foo1:
        test    rdx, rdx
        je      .L14
        movsx   esi, sil
        jmp     memset
.L14:
        ret

在程序员方面,优化的质量为80%。如果您像在问题中那样编写程序,则总是会得到较差的机器代码。

**** EDIT ****

-fno-builtin

void foo(char *buf, int bufval, size_t buflen)
{
    while(buflen--)
    {
        *buf++ = bufval;
    }
}


void foo1(char *buf, int bufval, size_t buflen)
{

    size_t l;
    for( l = 0; l < buflen; l++ )
    {
        buf[ l ] = bufval;
    }
}

foo:
        lea     rax, [rdi+rdx]
        test    rdx, rdx
        je      .L9
.L11:
        add     rdi, 1
        mov     BYTE PTR [rdi-1], sil
        cmp     rdi, rax
        jne     .L11
.L9:
        ret
foo1:
        lea     rax, [rdi+rdx]
        test    rdx, rdx
        je      .L15
.L17:
        mov     BYTE PTR [rdi], sil
        add     rdi, 1
        cmp     rdi, rax
        jne     .L17
.L15:
        ret