【问题标题】:implement _mm256_permutevar8x32_ps using AVX instructions使用 AVX 指令实现 _mm256_permutevar8x32_ps
【发布时间】:2014-08-10 23:22:42
【问题描述】:

AVX2 内部函数_mm256_permutevar8x32_ps 可以跨通道执行洗牌,这对于对长度为 8 的数组进行排序非常有用。

现在我只有 AVX(常春藤桥)并且希望在最短的周期内做同样的事情。请注意,数据和索引都是输入的,在编译时是未知的。

例如,数组是[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”后缀),因此不适合这种情况。

提前致谢。

【问题讨论】:

  • 如果你给出一个具体的例子来说明你想要做什么,那么回答你的问题会容易得多。
  • 啊……我明白你所说的不保持不变是什么意思。让我想一想,看看我能不能想出什么办法。
  • 我在我的答案中添加了在 AVX 中执行 _mm256_permutevar8x32_ps 的代码。

标签: c++ sse simd avx


【解决方案1】:

要在 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_pshas a zeroing feature 非常有用(另见 Intel Intrinsics Guide Online)。我在上面的代码中使用它来将第一条车道交换到第二条车道,然后将第一条车道归零。看 shifting-sse-avx-registers-32-bits-left-and-right-while-shifting-in-zeros 了解更多详情。

编辑:permutevar 内在函数允许运行时置换,因此不限于编译时间常量。下面的代码是来自Agner Fog's Vector Class Librarylookup8 函数。

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

【讨论】:

  • 感谢您的回答,我已经添加了一些细节。有什么想法吗?
猜你喜欢
  • 2018-07-29
  • 1970-01-01
  • 2013-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-06
  • 2017-05-08
相关资源
最近更新 更多