gpt4 book ai didi

simd - 使用变量来索引带有 _mm256_extract_epi32() 内在函数的 simd 向量

转载 作者:行者123 更新时间:2023-12-04 14:08:53 29 4
gpt4 key购买 nike

我正在使用 AVX 内在 _mm256_extract_epi32() .

不过,我不完全确定我是否正确使用它,因为 gcc 不喜欢我的代码,而 clang 编译它并运行它没有问题。

我根据整数变量的值提取车道,而不是使用常量。

当为 avx2 使用 clang3.8(或 clang4)编译以下代码段时,它 generates code并使用 vpermd操作说明。

#include <stdlib.h>
#include <immintrin.h>
#include <stdint.h>

uint32_t foo( int a, __m256i vec )
{
uint32_t e = _mm256_extract_epi32( vec, a );
return e*e;
}

现在,如果我改用 gcc,假设是 gcc 7.2,那么编译器将无法生成代码,并出现以下错误:
In file included from /opt/compiler-explorer/gcc-7.2.0/lib/gcc/x86_64-linux-gnu/7.2.0/include/immintrin.h:41:0,
from <source>:2:
/opt/compiler-explorer/gcc-7.2.0/lib/gcc/x86_64-linux-gnu/7.2.0/include/avxintrin.h: In function 'foo':
/opt/compiler-explorer/gcc-7.2.0/lib/gcc/x86_64-linux-gnu/7.2.0/include/avxintrin.h:524:20: error: the last argument must be a 1-bit immediate
return (__m128i) __builtin_ia32_vextractf128_si256 ((__v8si)__X, __N);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/compiler-explorer/gcc-7.2.0/lib/gcc/x86_64-linux-gnu/7.2.0/include/immintrin.h:37:0,
from <source>:2:
/opt/compiler-explorer/gcc-7.2.0/lib/gcc/x86_64-linux-gnu/7.2.0/include/smmintrin.h:449:11: error: selector must be an integer constant in the range 0..3
return __builtin_ia32_vec_ext_v4si ((__v4si)__X, __N);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

我有两个问题:
  • 为什么 clang 可以使用变量,并且 gcc 是否想要一个
    持续的?
  • 为什么gcc下不了决心?首先它需要一个 1 位立即数 ,后来它想要一个 0..3 范围内的整数常量 这些是不同的东西。

  • 顺便说一下,Intels Intrinsics Guide 没有指定对 _mm256_extract_epi32() 的索引值的约束,那么谁在这里,gcc 还是 clang?

    最佳答案

    显然 GCC 和 Clang 做出了不同的选择。
    恕我直言,海湾合作委员会做出了正确的选择,没有为可变索引实现这一点。内在_mm256_extract_epi32不会转换为单个指令。使用可变索引,此内在函数可能会导致代码效率低下,
    如果它用于性能关键循环。
    例如,Clang 3.8 需要 4 条指令来实现 _mm256_extract_epi32带有可变索引。
    GCC 迫使程序员考虑更高效的代码以避免 _mm256_extract_epi32带有可变索引。
    然而,有时有一个可移植的(gcc、clang、icc)函数很有用,它模拟 _mm256_extract_epi32带有变量索引:

    uint32_t mm256_extract_epi32_var_indx(const __m256i vec, const unsigned int i) {   
    __m128i indx = _mm_cvtsi32_si128(i);
    __m256i val = _mm256_permutevar8x32_epi32(vec, _mm256_castsi128_si256(indx));
    return _mm_cvtsi128_si32(_mm256_castsi256_si128(val));
    }
    内联后这应该编译为三个指令:两个 vmovd s 和 vpermd (gcc 8.2 带有 -m64 -march=skylake -O3 ):
    mm256_extract_epi32_var_indx:
    vmovd xmm1, edi
    vpermd ymm0, ymm1, ymm0
    vmovd eax, xmm0
    vzeroupper
    ret
    请注意,内在指南描述了索引的结果为 0
    >=8(无论如何这是一个不寻常的情况)。使用 Clang 3.8 和 mm256_extract_epi32_var_indx ,索引以模 8 减少。换句话说:仅使用索引的 3 个最低有效位。
    请注意,Clang 5.0 的内存往返也不是很有效,
    this Godbolt link .叮当 7.0
    编译失败 _mm256_extract_epi32带有可变索引。
    @Peter Cordes commented :具有固定索引 0、1、2 或 3,只有一个 pextrd需要指导
    从 xmm 寄存器中提取整数。对于固定索引 4、5、6 或 7,需要两条指令。
    不幸的是,一个 vpextrd不存在在 256 位 ymm 寄存器上工作的指令。

    下一个例子说明了我的答案:
    一个从 SIMD 内在函数开始的天真的程序员可能会写
    以下代码将元素 0, 1, ..., j-1 与 j<8 相加,来自 vec .
    #include <stdlib.h>
    #include <immintrin.h>
    #include <stdint.h>

    uint32_t foo( __m256i vec , int j)
    {
    uint32_t sum=0;
    for (int i = 0; i < j; i++){
    sum = sum + (uint32_t)_mm256_extract_epi32( vec, i );
    }
    return sum;
    }
    使用 Clang 3.8,这编译为大约 50 instructions带有分支和循环。 GCC 无法编译此代码。
    显然,对这些元素求和的有效代码可能基于:
  • 屏蔽掉元素 j、j+1、...、7 和
  • 计算水平总和。
  • 关于simd - 使用变量来索引带有 _mm256_extract_epi32() 内在函数的 simd 向量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48726032/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com