Implement _mm256_permutevar8x32_ps using AVX instructions

Internal _mm256_permutevar8x32_psAVX2 can perform shuffling in bands, which is very useful for sorting an array of length 8.

Now I only have AVX (Ivy Bridge), and I want to do the same in minimal cycles. Note that both the data and the index are entered and are unknown at compile time.

For example, an array [1,2,3,4,5,6,7,8], and indices [3,0,1,7,6,5,2,4], the output should be [4,1,2,8,7,6,3,5].

The control masks of the most convenient internal elements should be constants (without the suffix "var"), so in this case they are not suitable.

Thanks in advance.

+4
source share
1 answer

AVX, , _mm256_permute2f128_ps , . . , {1, 2, 3, 4, 5, 6, 7, 8} {0, 0, 1, 2, 3, 4, 5, 6}.

__m256 t0 = _mm256_permute_ps(x, _MM_SHUFFLE(1, 0, 3, 2));
__m256 t1 = _mm256_permute2f128_ps(t0, t0, 41);
__m256 y = _mm256_blend_ps(t0, t1, 0x33);

_mm256_permute2f128_ps , (. Intel Intrinsics Guide). , . shifting-sse-avx-registers-32-bits-left-and-right-while-shifting-in-zeros .

: permutevar , , . lookup8 Agner Fog Vector Class Library.

static inline Vec8f lookup8(Vec8i const & index, Vec8f const & table) {
#if INSTRSET >= 8 && VECTORI256_H > 1 // AVX2
#if defined (_MSC_VER) && _MSC_VER < 1700 && ! defined(__INTEL_COMPILER)        
    // bug in MS VS 11 beta: operands in wrong order. fixed in 11.0
    return _mm256_permutevar8x32_ps(_mm256_castsi256_ps(index), _mm256_castps_si256(table)); 
#elif defined (GCC_VERSION) && GCC_VERSION <= 40700 && !defined(__INTEL_COMPILER) && !defined(__clang__)
        // Gcc 4.7.0 has wrong parameter type and operands in wrong order. fixed in version 4.7.1
    return _mm256_permutevar8x32_ps(_mm256_castsi256_ps(index), table);
#else
    // no bug version
    return _mm256_permutevar8x32_ps(table, index);
#endif

#else // AVX
    // swap low and high part of table
    __m256  t1 = _mm256_castps128_ps256(_mm256_extractf128_ps(table, 1));
    __m256  t2 = _mm256_insertf128_ps(t1, _mm256_castps256_ps128(table), 1);
    // join index parts
    __m256i index2 = _mm256_insertf128_si256(_mm256_castsi128_si256(index.get_low()), index.get_high(), 1);
    // permute within each 128-bit part
    __m256  r0 = _mm256_permutevar_ps(table, index2);
    __m256  r1 = _mm256_permutevar_ps(t2,    index2);
    // high index bit for blend
    __m128i k1 = _mm_slli_epi32(index.get_high() ^ 4, 29);
    __m128i k0 = _mm_slli_epi32(index.get_low(),      29);
    __m256  kk = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_castsi128_ps(k0)), _mm_castsi128_ps(k1), 1);
    // blend the two permutes
    return _mm256_blendv_ps(r0, r1, kk);
#endif
}

get_low get_high:

Vec2db get_low() const {
    return _mm256_castpd256_pd128(ymm);
}
Vec2db get_high() const {
    return _mm256_extractf128_pd(ymm,1);
}
+4

Source: https://habr.com/ru/post/1545337/


All Articles