给定两个 uint8_t 数组,计算 128 个元素的 SAD

标签 c optimization simd avx avx2

我有两个 uint8_t 数组,它们都有 64 个元素。 我想出的“最好”方法是加载 4x 16 个元素,将它们放入两个 m128i 寄存器中,然后将它们都放入 >m256 注册。这是针对两个 uint8_t 数组完成的,如下所示:

__m128i a1, a2, b1, b2, s1, s2;
__m256i u, v, c;

// 128 bit of data x 2
a1 = _mm_set_epi64(*(__m64*)block1, *((__m64*)(block1 + stride)));
block1 += stride + stride;
a2 = _mm_set_epi64(*(__m64*)block1, *((__m64*)(block1 + stride)));

// the upper 128 bits of the result are undefined
u = _mm256_castsi128_si256(a1);
// Copy a to dst, then insert 128 bits from b into dst at the location specified by imm.
u = _mm256_insertf128_si256(u, a2, 0x1);

b1 = _mm_set_epi64(*(__m64*)block2, *((__m64*)(block2 + stride)));
block2 += stride + stride;
b2 = _mm_set_epi64(*(__m64*)block2, *((__m64*)(block2 + stride)));

// the upper 128 bits of the result are undefined
v = _mm256_castsi128_si256(b1);
// Copy a to dst, then insert 128 bits from b into dst at the location specified by imm.
v = _mm256_insertf128_si256(v, b2, 0x1);

我现在有两个m256寄存器,uv,并且可以计算SAD:

c = _mm256_sad_epu8(u, v);

但是,可能由于时间很晚,我无法想出更好的方法来获得结果...... 这就是我现在得到的:

s1 = _mm256_extractf128_si256(c, 0x0);
s2 = _mm256_extractf128_si256(c, 0x1);

int p, q;
p = _mm_extract_epi32(s1, 0x0);
q = _mm_extract_epi32(s1, 0x2);
*result += p + q;

p = _mm_extract_epi32(s2, 0x0);
q = _mm_extract_epi32(s2, 0x2);
*result += p + q;

result 是一个 int,如果不清楚的话。

这会生成相当多的指令。在我看来,这是加载我想要的所有单元的唯一方法。但是,这可能不是从 m256ic 寄存器中获取结果的最佳方式。

你说什么? 你能帮助我以更优化的方式做到这一点吗?

放在一起,该函数看起来像:

void foobar(uint8_t *block1, uint8_t *block2, int stride, int *result)
{
  *result = 0;
  int i;
  __m128i a1, a2, b1, b2, s1, s2;
  __m256i u, v, c;

  for (i = 0; i < 2; ++i) {
    // loading of uints
    // calculating SAD, and getting result

    block1 += stride; block2 += stride;
    block1 += stride; block2 += stride;
  }
}

由于 uint 组织方式的本质,我一次只能加载八个,然后我必须使用 stride 增加地址。一次加载(即加载 16 个)会产生不好的结果。

最佳答案

关于获取两个字节数组的绝对差之和,这是我使用 SSE 的方式:

__m128i sum1 = _mm_sad_epu8(u,v);
__m128i sum2 = _mm_shuffle_epi32(sum1,2);
__m128i sum3 = _mm_add_epi16(sum1,sum2);
int8_t  sum4 = (int8_t)_mm_cvtsi128_si32(sum3);

我现在无法在 AVX2 上测试它,但这是我会先尝试的未经测试的代码

__m256i sum1 = _mm256_sad_epu8(u,v);
__m256i sum2 = _mm256_shuffle_epi32(sum1,2);
__m256i sum3 = _mm256_add_epi16(sum1,sum2);  
__m128i sum4 = _mm_add_epi16(_mm256_castsi256_si128(sum3),
_mm256_extracti128_si256(sum3,1));
int8_t  sum5 = (int8_t)_mm_cvtsi128_si32(sum4);

我可以稍后测试。

关于给定两个 uint8_t 数组,计算 128 个元素的 SAD,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25676105/

相关文章:

python - Python中全局变量和局部变量的速度

floating-point - 为什么浮点寄存器不同于通用寄存器

javascript - C 中 JavaScript 的等效 call() apply() 是什么?

c - 运行冒泡排序后出现运行时错误

c++ - 无法在 Visual Studio 2012 中打开头文件

c - 相等或不重叠

c# - 优化 DateTime.Now 的替代方案

c - 英特尔内部函数 : multiply interleaved 8bit values

c - 如何使用 SSE 进行 uint32/float 转换?

C 计算递归序列 - 预期表达式错误