3

SSE2/SSE3 用に最適化された OS ポータブルを作成するmemcpyとしたら、どのようになりますか? GCC コンパイラと ICC コンパイラの両方をサポートしたいと考えています。私が尋ねる理由はmemcpy、glibc のアセンブラー コードで記述されており、SSE2/SSE3 用に最適化されておらず、他の一般的なmemcpy実装では、データの配置やサイズなどのシステム機能を十分に活用できない可能性があるためです。

これは、memcpyデータの配置を考慮し、SSE2用に最適化されていますが(私は思う)、SSE3用ではありません:

#ifdef __SSE2__
// SSE2 optimized memcpy()
void *CMemUtils::MemCpy(void *restrict b, const void *restrict a, size_t n)
{
    char *s1 = b;
    const char *s2 = a;
    for(; 0<n; --n)*s1++ = *s2++;
    return b;
}
#else
// Generic memcpy() implementation
void *CMemUtils::MemCpy(void *dest, const void *source, size_t count) const
{
#ifdef _USE_SYSTEM_MEMCPY
    // Use system memcpy()
    return memcpy(dest, source, count);
#else

    size_t blockIdx;
    size_t blocks = count >> 3;
    size_t bytesLeft = count - (blocks << 3);

    // Copy 64-bit blocks first
    _UINT64 *sourcePtr8 = (_UINT64*)source;
    _UINT64 *destPtr8 = (_UINT64*)dest;
    for (blockIdx = 0; blockIdx < blocks; blockIdx++) destPtr8[blockIdx] = sourcePtr8[blockIdx];

    if (!bytesLeft) return dest;

    blocks = bytesLeft >> 2;
    bytesLeft = bytesLeft - (blocks << 2);

    // Copy 32-bit blocks
    _UINT32 *sourcePtr4 = (_UINT32*)&sourcePtr8[blockIdx];
    _UINT32 *destPtr4 = (_UINT32*)&destPtr8[blockIdx];
    for (blockIdx = 0; blockIdx < blocks; blockIdx++) destPtr4[blockIdx] = sourcePtr4[blockIdx];

    if (!bytesLeft) return dest;

    blocks = bytesLeft >> 1;
    bytesLeft = bytesLeft - (blocks << 1);

    // Copy 16-bit blocks
    _UINT16 *sourcePtr2 = (_UINT16*)&sourcePtr4[blockIdx];
    _UINT16 *destPtr2 = (_UINT16*)&destPtr4[blockIdx];
    for (blockIdx = 0; blockIdx < blocks; blockIdx++) destPtr2[blockIdx] = sourcePtr2[blockIdx];

    if (!bytesLeft) return dest;

    // Copy byte blocks
    _UINT8 *sourcePtr1 = (_UINT8*)&sourcePtr2[blockIdx];
    _UINT8 *destPtr1 = (_UINT8*)&destPtr2[blockIdx];
    for (blockIdx = 0; blockIdx < bytesLeft; blockIdx++) destPtr1[blockIdx] = sourcePtr1[blockIdx];
    return dest;
#endif
}
#endif

すべてのmemcpy実装がスレッド セーフであるとは限りません。これは、独自のバージョンを作成するもう 1 つの理由です。memcpy以上のことから、SSE2/SSE3 用に最適化されたスレッドセーフな OS を可能な限り移植可能にするように少なくとも努めるべきであるという結論に至りました。

また、GCC がコンパイラ オプションを使用してアグレッシブなアンロールをサポートしていることも読みました-funroll-loopsが、重大なキャッシュ ミスがない場合、これにより SSE2 や SSE3 のパフォーマンスが向上する可能性がありますか?

32 ビット アーキテクチャと 64 ビット アーキテクチャで異なる memcpy バージョンを作成すると、パフォーマンスが向上しますか?

コピーする前に内部メモリ バッファを事前に調整すると、パフォーマンスが向上しますか?

#pragma loopSSE2/SSE3 自動パラレライザーによるループ コードの考慮方法を制御するには、どのように使用すればよいですか? #pragma loopfor() ループによって移動される連続したデータ領域で使用できると思われます。

独自の を追加するときにコンパイラに GCC のインライン化を強制する場合-fno-builtin-memcpyでも、GCC コンパイラ オプションを使用する必要がありますか? それとも、私のコードでオーバーライドするだけで十分ですか?-O3memcpymemcpymemcpy

更新: いくつかのテストの結果、最適化された SSE2memcpy()はそれほど高速ではなく、努力する価値があるように思えます。その点について、Intel C/C++ Compiler フォーラム で質問しました。

4

0 に答える 0