下面是我在搜索优化memcpy
实现时得到的代码。
这是链接
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);
}
Run Code Online (Sandbox Code Playgroud)
有人可以向我解释下面的行吗?
if (!(src & 0xFFFFFFFC) && !(dst & 0xFFFFFFFC))
Run Code Online (Sandbox Code Playgroud)
在这里,他们想检查src
和dst
地址是否与4 byte
边界对齐。他们为什么要使用!
,因为它false
每次都会造成条件?
其次,上面的代码是否还有进一步优化的余地?
本文虽然讨论了一个有趣的主题,但未能提供正确的示例。发布的代码被称为GNU 的 newlib 源代码。GNU 项目和 newlib 团队都会惊讶地发现这个意想不到的收敛声明!newlib不是一个 GNU 项目,它的大部分源代码都没有获得 GPL 许可。
这种优化的实现memcpy
是不可移植的、次优的并且在许多方面都是不正确的。
该测试if (!(src & 0xFFFFFFFC) && !(dst & 0xFFFFFFFC))
尝试检测src
和dst
地址是否在边界上对齐long
。由于多种原因,它很麻烦且不可移植,而且正如您所注意到的那样,它是完全错误的:
void *
到 的隐式转换int
是丑陋的并且定义了实现。为了更好的可移植性,应该对指针进行强制转换(uintptr_t)
。0xFFFFFFFC
假设类型long
为 4 字节。这可能是不正确的,事实上,long
在 64 位 Linux 和 Mac 系统上,类型的长度是 8 字节。src & 0xFFFFFFFC
不是对齐检查,也不太可能是0
,4 字节边界对齐的预期测试是src & 3
。此外,代码无法优化src
和dst
具有相同对齐但未在long
边界上对齐的情况。
其他可能的改进包括展开循环、使用小值的开关、将从slen
读取的字节组合起来,一旦其在边界上对齐就写入...src
long
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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,强制转换void *
在 C 中是不必要的,但在 C++ 中是必需的。
在尝试优化代码以提高速度时,请记住以下几点:
memcpy
通常在汇编中进行优化或由现代编译器作为内置实现。