了解memcpy()的实现

Ang*_*gus 11 c language-implementation memcpy

我正在寻找memcpy.c的实现,我发现了一个不同的memcpy代码.我无法理解他们为什么这样做(((地址)s)|((ADDRESS)d)| c)&(sizeof(UINT) - 1)

#if !defined(__MACHDEP_MEMFUNC)

#ifdef _MSC_VER
#pragma function(memcpy)
#undef __MEMFUNC_ARE_INLINED
#endif

#if !defined(__MEMFUNC_ARE_INLINED)
/* Copy C bytes from S to D.
 * Only works if non-overlapping, or if D < S.
 */
EXTERN_C void * __cdecl memcpy(void *d, const void *s, size_t c)
{
    if ((((ADDRESS) s) | ((ADDRESS) d) | c) & (sizeof(UINT) - 1)) {

        BYTE *pS = (BYTE *) s;
        BYTE *pD = (BYTE *) d;
        BYTE *pE = (BYTE *) (((ADDRESS) s) + c);

        while (pS != pE)
            *(pD++) = *(pS++);
    }
    else {
        UINT *pS = (UINT *) s;
        UINT *pD = (UINT *) d;
        UINT *pE = (UINT *) (BYTE *) (((ADDRESS) s) + c);

        while (pS != pE)
            *(pD++) = *(pS++);
    }
    return d;
}

#endif /* ! __MEMFUNC_ARE_INLINED */
#endif /* ! __MACHDEP_MEMFUNC */
Run Code Online (Sandbox Code Playgroud)

Eri*_*hil 14

代码正在测试地址是否适合于a UINT.如果是,则代码使用UINT对象进行复制.如果不是,则代码使用BYTE对象进行复制.

测试首先执行两个地址的按位OR.任一地址中的任何位都将在结果中打开.然后测试执行按位AND sizeof(UINT) - 1.预计a的大小UINT是2的幂.然后,大小减去1的所有低位都打开.例如,如果大小是4或8,那么小于4,或者是二进制11 2或111 2.如果任一地址不是a的大小的倍数UINT,那么它将打开其中一个位,并且测试将指示它.(通常,整数对象的最佳对齐方式与其大小相同.这不一定正确.应使用此代码的现代实现_Alignof(UINT) - 1而不是大小.)

复制UINT对象更快,因为在硬件级别,一个加载或存储指令加载或存储a的所有字节UINT(可能是四个字节).处理器通常在使用这些指令时比使用四倍数量的单字节加载或存储指令更快地复制.

这段代码当然是依赖于实现的; 它需要来自C实现的支持,它不是基本C标准的一部分,它取决于它执行的处理器的特定功能.

更高级的memcpy实现可以包含其他功能,例如:

  • 如果其中一个地址已对齐但另一个未对齐,则使用特殊的加载未对齐指令从一个地址加载多个字节,并将常规存储指令加载到另一个地址.
  • 如果处理器具有单指令多数据指令,则使用这些指令在单个指令中加载或存储许多字节(通常为16,可能更多).


Die*_*Epp 13

代码

((((ADDRESS) s) | ((ADDRESS) d) | c) & (sizeof(UINT) - 1))
Run Code Online (Sandbox Code Playgroud)

检查是否任s,dc不对齐的大小UINT.

例如,如果s = 0x7ff30b14,,d = 0x7ffa81d8c = 256,和sizeof(UINT) == 4,则:

s         = 0b1111111111100110000101100010100
d         = 0b1111111111110101000000111011000
c         = 0b0000000000000000000000100000000
s | d | c = 0b1111111111110111000101111011100
(s | d | c) & 3 =                        0b00
Run Code Online (Sandbox Code Playgroud)

所以两个指针都是对齐的.在两个对齐的指针之间复制内存更容易,而这仅使用一个分支.

在很多平台上,*(UINT *) ptr快,如果ptr被正确对齐的宽度UINT.在某些体系结构上,*(UINT *) ptr如果ptr未正确对齐,实际上会崩溃.

  • @EricAndres欢迎来到性能世界.那种东西到处都是.:) (3认同)
  • @EricAndres但当然,括号并不重要.理想情况下,你会看到`((ADDRESS)s)`和`((ADDRESS)d)`作为不透明的块`S`和'D`,所以你有`(S | D | c)&(sizeof) (UNIT) - 1)`,这不是_too_坏,然后你还有一个来自`if(...)...`的集合.表达式本身并不是以特别复杂的方式嵌套. (2认同)