【发布时间】:2014-12-21 14:31:56
【问题描述】:
我正在尝试使用 SSE 提出一个非常快速的阈值算法来替换它:
uint8_t *pSrc, *pDst;
// Assume pSrc and pDst point to valid data
// Handle left edge
*pDst++ = *pSrc++;
// Likeness filter
for (uint32_t k = 2; k < width; k++, pSrc++, pDst++)
if ((*pDst - *pSrc) * (*pDst - *pSrc) > 100 /*THRESHOLD_SQUARED*/) {
*pDst = *pSrc;
}
}
// Handle right edge
*pDst++ = *pSrc++;
到目前为止,我有这个:
const uint8_t THRESHOLD = 10;
__attribute__((aligned (16))) static const uint8_t mask[16] = {
THRESHOLD, THRESHOLD, THRESHOLD, THRESHOLD,
THRESHOLD, THRESHOLD, THRESHOLD, THRESHOLD,
THRESHOLD, THRESHOLD, THRESHOLD, THRESHOLD,
THRESHOLD, THRESHOLD, THRESHOLD, THRESHOLD
};
__m128i xmm1, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9;
xmm1 = _mm_load_si128((__m128i const *)mask);
xmm6 = _mm_setzero_si128();
uint8_t *pSrc, *pDst;
// Assume pSrc and pDst point to valid data
// I have other code with another mask for the first 16 entries
for (uint32_t k = 16; k < (width - 16); k += 16, pSrc += 16, pDst += 16) {
xmm3 = _mm_load_si128((__m128i const *)pDst);
xmm4 = _mm_load_si128((__m128i const *)pSrc);
xmm5 = _mm_unpacklo_epi8(xmm3, xmm6);
xmm7 = _mm_unpackhi_epi8(xmm3, xmm6);
xmm8 = _mm_unpacklo_epi8(xmm4, xmm6);
xmm9 = _mm_unpackhi_epi8(xmm4, xmm6);
xmm5 = _mm_sub_epi16(xmm5, xmm8);
xmm7 = _mm_sub_epi16(xmm7, xmm9);
xmm5 = _mm_abs_epi16(xmm5);
xmm7 = _mm_abs_epi16(xmm7);
xmm5 = _mm_packs_epi16(xmm5, xmm7);
xmm5 = _mm_cmpgt_epi8(xmm5, xmm1);
xmm3 = _mm_blendv_epi8(xmm3, xmm4, xmm5);
_mm_store_si128((__m128i *)pDst, xmm3);
}
// I have other code with another mask for the last 16 entries
我有想法使用另一种算法来处理两个值之差的绝对值(主要是留在U8(uchar)空间):
a' = a >> 1;
b' = b >> 1;
diff = (abs(sub(a' - b')) << 1) + ((a ^ b) & 1);
这需要 8 条 SSE 指令,而不是上面的 9 条(不包括编译器生成的任何额外寄存器移动),但我不确定它是否由于依赖延迟而更快。
是否有其他 SSE 专家有更好的建议(最高使用 SSE 4.2)?
更新 1 - 感谢 Yves 的建议!
const uint8_t THRESHOLD = 10;
__attribute__((aligned (16))) static const uint8_t mask[16] = {
THRESHOLD, THRESHOLD, THRESHOLD, THRESHOLD,
THRESHOLD, THRESHOLD, THRESHOLD, THRESHOLD,
THRESHOLD, THRESHOLD, THRESHOLD, THRESHOLD,
THRESHOLD, THRESHOLD, THRESHOLD, THRESHOLD
};
__m128i xmm1, xmm3, xmm4, xmm5, xmm6, xmm7;
xmm1 = _mm_load_si128((__m128i const *)mask);
xmm6 = _mm_setzero_si128();
uint8_t *pSrc, *pDst;
// Assume pSrc and pDst point to valid data
// I have other code with another mask for the first 16 entries
for (uint32_t k = 16; k < (width - 16); k += 16, pSrc += 16, pDst += 16) {
xmm3 = _mm_load_si128((__m128i const *)pDst);
xmm4 = _mm_load_si128((__m128i const *)pSrc);
xmm5 = _mm_subs_epu8(xmm3, xmm4);
xmm7 = _mm_subs_epu8(xmm4, xmm3);
xmm5 = _mm_adds_epu8(xmm5, xmm7);
xmm5 = _mm_subs_epu8(xmm5, xmm1);
xmm5 = _mm_cmpeq_epi8(xmm5, xmm6);
xmm4 = _mm_blendv_epi8(xmm4, xmm3, xmm5);
_mm_store_si128((__m128i *)pDst, xmm4);
}
// I have other code with another mask for the last 16 entries
【问题讨论】:
-
为什么将标签从 SSE 切换到矢量化? SSE 应该是您的标签之一。
-
两个代码 sn-ps 不做同样的事情(C 代码未定义未比较的值,而 SSE 代码将它们设置为零)。此外,几乎可以肯定,内存带宽受限于 SSE 阈值这样简单的操作,因此无论如何不要期望算法之间有太大差异。
-
@Damon,多亏了混合指令,Dst 值得以保留。
-
让我们假设 pSrc 和 pDst 都在一个足够高的缓存中,内存带宽不是一个大问题。
-
顺便说一句,我检查了 SSE 代码的正确性(新旧与 C 代码)。它们都产生相同的值。
标签: performance algorithm optimization sse simd