为什么 FreeBSD 的 memchr 实现会在其条件下增加其指针?
Why does FreeBSD's implementation of memchr increment its pointer in its condition?
FreeBSD's generic implementation of 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;
使用条件内联 post 增量对某些 compiler/platform 有优化好处吗?
首先:这纯属猜测。我没有写过这段代码,也无法验证我的猜测。
这两个版本的代码之间存在一个非常重要的语义差异:
// 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
生成的分支指令之前。至少我们可以在 IMO 假设编写代码时(1988 年?)的编译器将 C 代码相对直接地转换为汇编代码。
Does inlining the post-increment with the condition have some
optimization benefit for some compiler/platform?
在分支之前增加增量允许在其分支指令具有 delay slot 的体系结构上进行相对简单的优化:您可以将增量移动到该延迟槽而不是在那里有一个 NOP。
所以版本 A 比版本 B 每次循环迭代需要一条指令,代价是函数 returns 时单次递减。这是一个(微)优化。
指针post增量的情况是为了让那个时代的编译器(例如PCC)在DEC机器中使用自动增量寻址模式(就像Mark Plotnick在评论中提到的那样)。
由于所有DEC机器都支持自增寻址,这种编码循环的方式曾经很普遍(顺便说一句,m68k支持同样的优化)。
另一方面,do-while
循环仅仅是因为在一个简单的编译器上它往往会产生更好的代码,而不仅仅是为了避免设置 p
.
都不应该对现代编译器有任何影响。
FreeBSD's generic implementation of 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;
使用条件内联 post 增量对某些 compiler/platform 有优化好处吗?
首先:这纯属猜测。我没有写过这段代码,也无法验证我的猜测。
这两个版本的代码之间存在一个非常重要的语义差异:
// 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
生成的分支指令之前。至少我们可以在 IMO 假设编写代码时(1988 年?)的编译器将 C 代码相对直接地转换为汇编代码。
Does inlining the post-increment with the condition have some optimization benefit for some compiler/platform?
在分支之前增加增量允许在其分支指令具有 delay slot 的体系结构上进行相对简单的优化:您可以将增量移动到该延迟槽而不是在那里有一个 NOP。
所以版本 A 比版本 B 每次循环迭代需要一条指令,代价是函数 returns 时单次递减。这是一个(微)优化。
指针post增量的情况是为了让那个时代的编译器(例如PCC)在DEC机器中使用自动增量寻址模式(就像Mark Plotnick在评论中提到的那样)。
由于所有DEC机器都支持自增寻址,这种编码循环的方式曾经很普遍(顺便说一句,m68k支持同样的优化)。
另一方面,do-while
循环仅仅是因为在一个简单的编译器上它往往会产生更好的代码,而不仅仅是为了避免设置 p
.
都不应该对现代编译器有任何影响。