【问题标题】:Compress mask using AVX intrinsics使用 AVX 内部函数压缩掩码
【发布时间】:2014-07-12 19:36:27
【问题描述】:

我想将两个包含掩码的 256 位向量 (__m256d) 合并为一个 256 位向量,方法是省略每个向量的上半部分。 64 位双精度。

所以,如果在下面,a_i, b_i, ... 是 32 位字,我有两个 256 位(4 x 双)向量,它们具有以下结构:

a_0, a_0, b_0, b_0, c_0, c_0, d_0, d_0a_1, a_1, b_1, b_1, c_1, c_1, d_1, d_1

我想要一个具有以下结构的 256 位向量:

a_0, b_0, c_0, d_0, a_1, b_1, c_1, d_1.

如何使用英特尔内在函数有效地做到这一点?可用的指令集包括 AVX。

【问题讨论】:

  • 我假设您打算用 _mm_cmp_pd 代替 __mm_cmp_pd。但是 _mm_cmp_pd 返回 __m128d。

标签: c x86 simd intrinsics avx


【解决方案1】:

看起来您可以利用这样一个事实,即全 1 的位模式在单精度和双精度下都是 NaN,类似地,全 0 的位模式在两种情况下都是 0.0。因此,要将两个双掩码向量打包成一个浮点向量,您可以这样做:

 __m256 v = _mm256_set_m128(_mm256_cvtpd_ps(v0), _mm256_cvtpd_ps(v1));

请注意,如果您没有_mm256_set_m128,那么您可以将其定义为:

#define _mm256_set_m128(va, vb) \
        _mm256_insertf128_ps(_mm256_castps128_ps256(vb), va, 1)

这是一个演示:

#include <stdio.h>
#include <immintrin.h>

#define _mm256_set_m128(va, vb) \
        _mm256_insertf128_ps(_mm256_castps128_ps256(vb), va, 1)

static void printvd(const char * label, __m256d v)
{
    int64_t a[4];
    _mm256_storeu_pd((double *)a, v);
    printf("%s = %lld %lld %lld %lld\n", label, a[0],  a[1],  a[2],  a[3]);
}

static void printvf(const char * label, __m256 v)
{
    int32_t a[8];
    _mm256_storeu_ps((float *)a, v);
    printf("%s = %d %d %d %d %d %d %d %d\n", label, a[0],  a[1],  a[2],  a[3],  a[4],  a[5],  a[6],  a[7]);
}

int main()
{
    __m256d v0 = _mm256_set_pd(0.0, 1.0, 2.0, 3.0);
    __m256d v1 = _mm256_set_pd(3.0, 2.0, 1.0, 0.0);
    __m256d vcmp0 = _mm256_cmp_pd(v0, v1, 1);
    __m256d vcmp1 = _mm256_cmp_pd(v1, v0, 1);
    __m256 vcmp = _mm256_set_m128(_mm256_cvtpd_ps(vcmp0), _mm256_cvtpd_ps(vcmp1));
    printvd("vcmp0", vcmp0);
    printvd("vcmp1", vcmp1);
    printvf("vcmp ", vcmp);
    return 0;
}

测试:

$ gcc -Wall -mavx so_avx_test.c && ./a.out
vcmp0 = 0 0 -1 -1
vcmp1 = -1 -1 0 0
vcmp  = -1 -1 0 0 0 0 -1 -1

【讨论】:

  • 没有 AVX2 很难做得更好。
  • 我唯一定义 _mm256_set_m128() 的编译器是 Visual Studio 2013,由于参数类型,它不接受此代码。转换内在函数是否正确?
  • @ScottD:没问题 - 我在上面的答案中添加了 _mm256_set_m128 的宏。
  • 感谢 _mm256_set_m128 宏,这与 VS2013 使用的宏相同。但问题仍然存在:由于参数类型不正确,代码无法构建。
  • 抱歉 - 有一个小错字(__m256d 应该是 __m256),我现在已经修复了。我还添加了演示代码,这似乎可以工作(至少使用我的编译器)。
【解决方案2】:

在下面的代码中,function1() 执行操作。主程序提供样本数据并打印结果。样本数据的 FFFFFFFF 部分是要省略的上半部分。样本数据的其余双字包含独特的模式。程序输出为:

v0=A0000000 FFFFFFFF B0000000 FFFFFFFF C0000000 FFFFFFFF D0000000 FFFFFFFF
v1=A0000001 FFFFFFFF B0000001 FFFFFFFF C0000001 FFFFFFFF D0000001 FFFFFFFF
vr=A0000000 B0000000 C0000000 D0000000 A0000001 B0000001 C0000001 D0000001

代码在 VS2013 中使用命令行 cl /Ox /arch:AVX sample.c 进行了测试,在 gcc 4.9.0 中使用命令行 gcc -O3 -mavx -c sample.c 进行了测试。

AVX 有限的跨车道能力使得解决方案相对复杂。

#include <intrin.h>
#include <stdint.h>
#include <stdio.h>

//---------------------------------------------------------------------------

static void dump (void *data)
    {
    uint32_t *d32 = data;
    int index;

    for (index = 0; index < 8; index++)
        printf ("%08X ", d32 [index]);
    printf ("\n");
    }

//---------------------------------------------------------------------------

 static __m256d function1 (__m256d v0, __m256d v1)
    {
    __m256d tmp0 = _mm256_permute2f128_pd (v0, v1, 0x20);
    __m256d tmp1 = _mm256_permute2f128_pd (v0, v1, 0x31);
    return _mm256_castps_pd (_mm256_shuffle_ps (_mm256_castpd_ps (tmp0), _mm256_castpd_ps (tmp1), 0x88));
    }

//---------------------------------------------------------------------------

int main (void)
    {
    __m256d v0, v1, vr;

    v0 = _mm256_castsi256_pd (_mm256_set_epi32 (0xffffffff, 0xd0000000, 0xffffffff, 0xc0000000, 0xffffffff, 0xb0000000, 0xffffffff, 0xa0000000));
    v1 = _mm256_castsi256_pd (_mm256_set_epi32 (0xffffffff, 0xd0000001, 0xffffffff, 0xc0000001, 0xffffffff, 0xb0000001, 0xffffffff, 0xa0000001));
    vr = function1 (v0, v1);
    printf ("v0="); dump (&v0);
    printf ("v1="); dump (&v1);
    printf ("vr="); dump (&vr);
    return 0;
    }

【讨论】:

    猜你喜欢
    • 2017-12-23
    • 1970-01-01
    • 1970-01-01
    • 2013-12-25
    • 2020-11-19
    • 2017-09-22
    • 2015-05-27
    • 2013-02-12
    相关资源
    最近更新 更多