c - 是否有一个内部函数可以将 __m128i vector 的最后 n 个字节清零?

标签 c vectorization sse simd

给定n ,我想将最后一个 n 归零__m128i 的字节数 vector 。

例如考虑以下 __m128i vector :

11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111

将最后一个 n = 4 归零后字节, vector 应如下所示:

11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00000000 00000000 00000000

是否有一个 SSE 内部函数可以执行此操作(通过接受 __128i vector 和 n 作为参数)?

最佳答案

有多种不依赖 AVX512 的选项。例如:

未对齐的负载

char mask[32] = { 0, 0, 0, 0, 0, 0, 0, 0,
                  0, 0, 0, 0, 0, 0, 0, 0,
                  -1, -1, -1, -1, -1, -1, -1, -1,
                  -1, -1, -1, -1, -1, -1, -1, -1};

__m128i zeroLowestNBytes(__m128i x, uint32_t n)
{
    __m128i m = _mm_loadu_si128((__m128i*)&mask[16 - n]);
    return _mm_and_si128(x, m);
}

使用 AVX,加载可以成为 vpand 的内存操作数。没有 AVX 它仍然很好,使用 movdqupand

负载未对齐通常不是问题,除非它跨越 4K 边界。如果您可以使 mask 32 对齐,那么该问题就会消失。负载仍未对齐,但不会达到特定的边缘情况。

n 是一个 uint32_t 以避免符号扩展。

广播和比较

__m128i zeroLowestNBytes(__m128i x, int n)
{
    __m128i threshold = _mm_set1_epi8(n);
    __m128i index = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
    return _mm_andnot_si128(_mm_cmpgt_epi8(threshold, index), x);
}

这避免了未对齐的负载,但这并不重要。更重要的是,它避免了“输入依赖负载”:在具有未对齐负载的版本中,负载取决于n。在此版本中,负载与 n 无关。例如,如果此函数是内联的,则允许编译器将其提升到循环之外。它还允许无序执行更自由地尽早开始加载,可能在计算 n 之前。

不利的一面是,它基本上需要 AVX2 或 SSSE3 才能像样地实现 _mm_set1_epi8(n)。此外,这通常会花费更多的指令,这可能会降低吞吐量。延迟应该更好,因为“主链”中没有负载(有负载,但它在一边,它不会将其延迟添加到计算延迟中)。

关于c - 是否有一个内部函数可以将 __m128i vector 的最后 n 个字节清零?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63582402/

相关文章:

x86 - SSE 乘法 16 x uint8_t

C - 调用函数时参数错误

c - 哈希算法的实现将字符串转换为数字

python - 测量 numpy 2d 区域之间的边界重叠

c++ - 使用 SSE 计算矩阵乘积比使用直接算法慢得多

c++ - 使用 SSE 获取 __m128i vector 中的最小短值?

c - 我不断收到段错误

c - 如何交换位位置值 12 34 56 78?

c - 如何在 C 语言中使用 SSE 内在函数计算单 vector 点积

r - R中的元素绑定(bind)