我正在尝试比较两行pixel。
pixel定义为包含4个float值(RGBA值)的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和所有的优化标志进行编译。
发布于 2013-02-10 19:25:29
我已经用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;
}大致是这样的。
发布于 2013-02-10 19:02:39
您可能想要检查此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]);
....发布于 2013-02-10 18:12:57
我不能直接帮助你,因为我正在使用Mac,但有一个简单的方法可以弄清楚发生了什么:
您只需在调试模式下进入memcpy,然后切换到反汇编视图。由于memcpy是一个简单的小函数,您将很容易弄清楚所有的实现技巧。
https://stackoverflow.com/questions/14796562
复制相似问题