使用AVX指令实现_mm256_permutevar8x32_ps函数

5
AVX2内置函数_mm256_permutevar8x32_ps可以在通道之间执行洗牌操作,这对于长度为8的数组排序非常有用。
现在我只有AVX(Ivy Bridge),并希望以最少的周期完成相同的操作。请注意,数据和索引都是输入且在编译时未知的。
例如,数组是[1,2,3,4,5,6,7,8],索引是[3,0,1,7,6,5,2,4],输出应为[4,1,2,8,7,6,3,5]
大多数方便的内置函数的控制掩码必须是常量(没有“var”后缀),因此在这种情况下不适用。
提前感谢您的帮助。

1
如果您提供一个具体的例子,那么回答您的问题会更容易些。 - Z boson
https://dev59.com/o3jZa4cB1Zd3GeqPbkD- - Z boson
啊...我明白你的意思,不是常量的问题。让我想一想,看看能否想出什么解决方案。 - Z boson
我在我的回答中添加了AVX执行_mm256_permutevar8x32_ps的代码。 - Z boson
1个回答

5

在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 Online)。我在上面的代码中使用它来交换第一条车道和第二条车道,然后将第一条车道清零。更多详情,请参见shifting-sse-avx-registers-32-bits-left-and-right-while-shifting-in-zeros

编辑:permutevar内置函数允许运行时排列,因此不限于编译时常量。下面的代码是Agner Fog's Vector Class Library中的lookup8函数。

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_lowget_high函数:

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

谢谢回答,我已经添加了一些详细信息。有什么想法吗? - lzhang3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接