如果我要编写一个针对SSE2 / SSE3优化的便携式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
实现都是线程安全的,这只是制作我们自己版本的另一个原因。所有这一切使我得出结论,我至少应该尝试创建一个线程安全的OS可移植memcpy
,它针对SSE2 / SSE3进行了优化。
我还读过GCC支持使用-funroll-loops
编译器选项的积极展开,如果没有重大的缓存未命中,这可以提高SSE2和/或SSE3的性能吗?
为32位和64位架构制作不同的memcpy版本是否有性能提升?
复制前预先校准内部存储器缓冲区是否有任何性能提升?
如何使用#pragma loop
控制SSE2 / SSE3自动并行化程序如何考虑循环代码?据说可以通过for()循环移动连续数据区域#pragma loop
。
我是否需要使用GCC编译器选项-fno-builtin-memcpy
甚至使用-O3
来强制编译器在添加我自己的memcpy
时内联GCC memcpy
?或者只是在我的代码中覆盖memcpy
就足够了?
更新:
经过一些测试后,在我看来,SSE2优化memcpy()
并不是那么快,值得付出努力。我问过question in that regard on the Intel C/C++ Compiler forums。