我想比较两行pixel
s。
pixel
定义为struct
,其中包含4个float
值(RGBA)。
我没有使用memcmp
的原因是因为我需要返回第一个不同像素的位置,memcmp
不会这样做。
我的第一个实现使用SSE
内在函数,比memcmp
慢约30%:
inline int PixelMemCmp(const Pixel* a, const Pixel* b, int count)
{
for (int i = 0; i < count; i++)
{
__m128 x = _mm_load_ps((float*)(a + i));
__m128 y = _mm_load_ps((float*)(b + i));
__m128 cmp = _mm_cmpeq_ps(x, y);
if (_mm_movemask_ps(cmp) != 15) return i;
}
return -1;
}
然后我发现将值视为整数而不是浮点数加速了一些事情,现在只比memcmp
慢了约20%。
inline int PixelMemCmp(const Pixel* a, const Pixel* b, int count)
{
for (int i = 0; i < count; i++)
{
__m128i x = _mm_load_si128((__m128i*)(a + i));
__m128i y = _mm_load_si128((__m128i*)(b + i));
__m128i cmp = _mm_cmpeq_epi32(x, y);
if (_mm_movemask_epi8(cmp) != 0xffff) return i;
}
return -1;
}
根据我在其他问题上的阅读,memcmp
的MS实施也是使用SSE
实现的。我的问题是,MS实现的其他技巧是什么呢?我不这样做?即使它进行逐字节比较,它仍然如何更快?
对齐是一个问题吗?如果pixel
包含4个浮点数,那么是否已经在16字节边界上分配了一个像素数组?
我正在使用/o2
和所有优化标记进行编译。
答案 0 :(得分:3)
你可能想要检查这个memcmp SSE implementation,特别是__sse_memcmp
函数,它从一些健全性检查开始,然后检查指针是否对齐:
aligned_a = ( (unsigned long)a & (sizeof(__m128i)-1) );
aligned_b = ( (unsigned long)b & (sizeof(__m128i)-1) );
如果它们不对齐,它会逐字节地比较指针,直到对齐地址的开始:
while( len && ( (unsigned long) a & ( sizeof(__m128i)-1) ) )
{
if(*a++ != *b++) return -1;
--len;
}
然后将剩余内存与SSE指令进行比较,类似于您的代码:
if(!len) return 0;
while( len && !(len & 7 ) )
{
__m128i x = _mm_load_si128( (__m128i*)&a[i]);
__m128i y = _mm_load_si128( (__m128i*)&b[i]);
....
答案 1 :(得分:3)
我已经用SSE(和MMX / 3DNow!)编写了strcmp / memcmp优化,第一步是确保数组尽可能对齐 - 你可能会发现你必须做第一次和/或最后一次字节“一次一个”。
如果您可以在数据到达循环之前对齐[如果您的代码进行了分配],那么这是理想的。
第二部分是展开循环,所以你不会得到这么多“如果循环不在最后,跳回循环的开头” - 假设循环很长。
您可能会发现在执行“我们现在离开”条件之前预加载输入的下一个数据也有帮助。
编辑:最后一段可能需要一个例子。此代码假设展开的循环至少为两个:
__m128i x = _mm_load_si128((__m128i*)(a));
__m128i y = _mm_load_si128((__m128i*)(b));
for(int i = 0; i < count; i+=2)
{
__m128i cmp = _mm_cmpeq_epi32(x, y);
__m128i x1 = _mm_load_si128((__m128i*)(a + i + 1));
__m128i y1 = _mm_load_si128((__m128i*)(b + i + 1));
if (_mm_movemask_epi8(cmp) != 0xffff) return i;
cmp = _mm_cmpeq_epi32(x1, y1);
__m128i x = _mm_load_si128((__m128i*)(a + i + 2));
__m128i y = _mm_load_si128((__m128i*)(b + i + 2));
if (_mm_movemask_epi8(cmp) != 0xffff) return i + 1;
}
大致类似的东西。
答案 2 :(得分:0)
我无法直接帮助你,因为我正在使用Mac,但有一种简单的方法可以弄清楚会发生什么:
您只需在调试模式下进入memcpy并切换到反汇编视图。由于memcpy是一个简单的小函数,你很容易弄清楚所有的实现技巧。