Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compress mask using AVX intrinsics

I'd like to combine two 256 bit vectors (__m256d) which contain the masks as a result of a comparison-operation (such as _mm256_cmp_pd) to one 256 bit vector, by omitting the upper half of every 64 bit double.

So, if in the following, a_i, b_i, ... are 32 bit words, and I have two 256 bit (4 x double) vectors that have the following structure:

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

I'd like to have a single 256 bit vector with the following structure:

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

How do I do this efficiently using Intel intrinsics? The instruction set available is everything up to AVX.

like image 683
dsd Avatar asked May 23 '14 12:05

dsd


1 Answers

It looks like you can exploit the fact that a bit pattern of all 1s is a NaN in both single and double precision, and similarly a bit pattern of all 0s is a 0.0 in both cases. So to pack your two double mask vectors to a single float vector you can do this:

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

Note that if you do not have _mm256_set_m128 then you can define it as:

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

Here's a demo:

#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;
}

Test:

$ 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
like image 111
Paul R Avatar answered Sep 30 '22 08:09

Paul R