2017-02-28 38 views
6

FreeBSD's generic implementation of memchr做:为什么FreeBSD的memchr实现在它的条件下增加它的指针?

void * 
memchr(const void *s, int c, size_t n) 
{ 
    if (n != 0) { 
     const unsigned char *p = s; 

     do { 
      if (*p++ == (unsigned char)c) 
       return ((void *)(p - 1)); 
     } while (--n != 0); 
    } 
    return (NULL); 
} 

这对我来说似乎是不必要的复杂;最初的n != 0检查与do - while只是为了避免p声明似乎完全没有意义。然而,我在为什么循环体确实特别感兴趣:

if (*p++ == (unsigned char)c) 
    return ((void *)(p - 1)); 

,而不是更直截了当:

if (*p == (unsigned char)c) 
    return ((void *) p); 
++p; 

是否内联后递增与病情有一定优化的好处对于一些编译器/平台?

+1

您是否检查编译器生成的机器码(使用优化)?这与你的方法有什么不同?'p'的_definition_是否生成代码?是的,你会错过考虑最可能编写代码的时间以及当时优化编译器的优劣。相当多的CPU有“减量和分支如果不是零”指令。 – Olaf

+0

@Olaf我确实使用godbolt来比较实现和gcc(使用-O3,再次使用-Os)。我不擅长汇编,但直截了当的版本似乎产生较少的指令。 – jamesdlin

+0

重新阅读我的评论,我增加了很多。你检查每个运行BSD的目标? x86并不是一个广泛传播的平台。该代码甚至可以用于8位和16位MCU,其数量超过x86和ARM数十年。 – Olaf

回答

2

指针后递增的情况下是允许的时代(例如PCC)的编译器使用的DEC计算机自动递增寻址模式(像马克Plotnick在评论中提到)。

由于所有的DEC计算机支持自动增量寻址,编码循环的这种方式曾经是很常见的(顺便说一句,m68k的支持相同的优化)。

另一方面,do-while循环仅仅是因为在naïf编译器上它倾向于产生更好的代码,而不仅仅是为了避免设置p

也不应该做一个现代的编译器有什么区别。

+0

避免设置'p'的确保存了一条指令,当然是 –

+0

对于naïf编译器来说,它没有任何区别,它只改变了分支的位置,这只对'n = 0'很重要。 –

3

第一:这是纯粹的猜测。我没有写这个代码,也不能验证我的猜测。

有代码的这两个版本之间的一个非常重要的语义差别:

// Version A 
if (*p++ == (unsigned char)c) 
    return ((void *)(p - 1)); 

// Version B 
if (*p == (unsigned char)c) 
    return ((void *) p); 
++p; 

在版本A中if的代码块之前的增量测序,而版本B的是后测序块。

因此,在版本A中的增量代码将其很可能从if生成的分支指令之前。至少我们可以将国际海事组织从编写代码时(1988年?)的编译器假设这种从C代码到汇编的相对直接翻译。

是否与条件内联后增有一些编译器/平台的一些 优化中受益?

有增量的分支允许相对简单的优化架构上,其分支指令具有前delay slot:您可以将增量成延迟槽代替具有NOP那里。

所以版本A需要每循环迭代一个指令小于版本B,在一个单一的递减函数返回时的成本。这是一个(微)优化。

相关问题