下面是我在搜索优化的memcpy
实现时获得的代码。
这是link
void *memcpy(void *dst, void const *src, size_t len) {
long *plDst = (long *)dst;
long const *plSrc = (long const *)src;
if (!(src & 0xFFFFFFFC) && !(dst & 0xFFFFFFFC)) {
while (len >= 4) {
*plDst++ = *plSrc++;
len -= 4;
}
}
char *pcDst = (char *)plDst;
char const *pcDst = (char const *)plSrc;
while (len--) {
*pcDst++ = *pcSrc++;
}
return (dst);
}
有人可以向我解释以下内容吗?
if (!(src & 0xFFFFFFFC) && !(dst & 0xFFFFFFFC))
在这里,他们想检查src
和dst
的地址是否与4 byte
的边界对齐。他们为什么使用!
,因为每次都会产生条件false
?
第二,以上代码中是否还有进一步优化的空间?
答案 0 :(得分:2)
本文虽然讨论了一个有趣的主题,但却没有提供正确的示例。发布的代码称为 GNU的newlib源代码。 GNU项目和newlib团队都将惊讶地得知这一意外的声明! newlib 不是GNU项目,并且其大部分源代码均未获得GPL许可。
此memcpy
的优化实现是不可移植的,次优的,在许多方面都是不正确的。
测试if (!(src & 0xFFFFFFFC) && !(dst & 0xFFFFFFFC))
试图检测src
和dst
地址是否在long
边界上对齐。您会注意到,它繁琐且不便于携带,并且完全错误:
void *
到int
的(uintptr_t)
,以实现更好的可移植性。0xFFFFFFFC
假设类型long
为4个字节。这可能是不正确的,并且在64位linux和Mac系统上,类型long
的确是8字节长。src & 0xFFFFFFFC
不是对对齐方式的检查,不太可能是0
,针对4字节边界的对齐方式的预期测试是src & 3
。此外,代码无法优化src
和dst
具有相同对齐方式但未在long
边界对齐的情况。
其他可能的改进包括展开循环,使用一个用于len
的较小值的开关,将从src
读取的字节组合到long
s中,一旦写入dst
在long
边界上对齐...
这是一种改进的替代方法:
#include <stdint.h>
void *memcpy(void *dst, void const *src, size_t len) {
unsigned char *pcDst = (unsigned char *)dst;
unsigned char const *pcSrc = (unsigned char const *)src;
if (len >= sizeof(long) * 2
&& ((uintptr_t)src & (sizeof(long) - 1)) == ((uintptr_t)dst & (sizeof(long) - 1))) {
while (((uintptr_t)pcSrc & (sizeof(long) - 1)) != 0) {
*pcDst++ = *pcSrc++;
len--;
}
long *plDst = (long *)pcDst;
long const *plSrc = (long const *)pcSrc;
/* manually unroll the loop */
while (len >= sizeof(long) * 4) {
plDst[0] = plSrc[0];
plDst[1] = plSrc[1];
plDst[2] = plSrc[2];
plDst[3] = plSrc[3];
plSrc += 4;
plDst += 4;
len -= sizeof(long) * 4;
}
while (len >= sizeof(long)) {
*plDst++ = *plSrc++;
len -= sizeof(long);
}
pcDst = (unsigned char *)plDst;
pcSrc = (unsigned char const *)plSrc;
}
while (len--) {
*pcDst++ = *pcSrc++;
}
return dst;
}
请注意,void *
中的强制转换在C中是不必要的,但在C ++中是必需的。
在尝试优化代码以提高速度时,请牢记以下几点:
memcpy
通常在汇编中进行优化,或者由现代编译器内置实现。