11

memcpy.c の実装を探していたところ、別の memcpy コードが見つかりました。なぜ彼らが (((ADDRESS) 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 */
4

2 に答える 2

14

このコードは、アドレスが a に対して適切に配置されているかどうかをテストしていますUINT。その場合、コードはオブジェクトを使用してコピーしUINTます。そうでない場合、コードはオブジェクトを使用してコピーしBYTEます。

このテストは、最初に 2 つのアドレスのビットごとの OR を実行することによって機能します。いずれかのアドレスでオンになっているビットは、結果でオンになります。次に、テストは とのビットごとの AND を実行しsizeof(UINT) - 1ます。a のサイズはUINT2 のべき乗であると予想されます。次に、サイズから 1 を引いた値の下位ビットがすべてオンになります。たとえば、サイズが 4 または 8 の場合、それより 1 小さい、バイナリ 11 2または 111 2です。いずれかのアドレスが a のサイズの倍数でない場合、UINTこれらのビットのいずれかがオンになり、テストでそれが示されます。(通常、整数オブジェクトの最適なアラインメントはそのサイズと同じです。これは必ずしも正しいとは限りません。このコードの最新の実装で_Alignof(UINT) - 1は、サイズの代わりに使用する必要があります。)

UINTハードウェア レベルでは、1 つのロードまたはストア命令が a のすべてのバイトUINT(おそらく 4 バイト)をロードまたはストアするため、オブジェクトを使用したコピーはより高速です。これらの命令を使用すると、プロセッサは通常、4 倍のシングルバイト ロードまたはストア命令を使用する場合よりも高速にコピーします。

もちろん、このコードは実装に依存します。ベースC標準の一部ではないC実装からのサポートが必要であり、実行するプロセッサの特定の機能に依存します。

より高度なmemcpy実装には、次のような追加機能を含めることができます。

  • アドレスの 1 つがアライメントされていて、もう 1 つがアライメントされていない場合は、特別な非アライメント ロード命令を使用して、一方のアドレスから複数のバイトをロードし、通常のストア命令を他方のアドレスにロードします。
  • プロセッサに単一命令複数データ命令がある場合は、それらの命令を使用して、1 つの命令で多くのバイト (多くの場合 16、場合によってはそれ以上) をロードまたは格納します。
于 2013-10-04T17:47:51.630 に答える
13

コード

((((ADDRESS) s) | ((ADDRESS) d) | c) & (sizeof(UINT) - 1))

sd、または のいずれかcが のサイズに整列していないかどうかを確認しますUINT

たとえば、s = 0x7ff30b14d = 0x7ffa81d8、 c = 256、およびsizeof(UINT) == 4の場合:

s         = 0b1111111111100110000101100010100
d         = 0b1111111111110101000000111011000
c         = 0b0000000000000000000000100000000
s | d | c = 0b1111111111110111000101111011100
(s | d | c) & 3 =                        0b00

したがって、両方のポインターが整列されます。両方がアラインされているポインター間でメモリをコピーする方が簡単で、これは 1 つの分岐のみで行われます。

多くのアーキテクチャで*(UINT *) ptrは、 が a の幅に正しく配置されている場合、はるかに高速です。一部のアーキテクチャでは、 が正しく配置されていないと、実際にクラッシュします。ptrUINT*(UINT *) ptrptr

于 2013-10-04T17:47:24.940 に答える