首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用变量索引带有_mm256_extract_epi32()内禀的simd向量

使用变量索引带有_mm256_extract_epi32()内禀的simd向量
EN

Stack Overflow用户
提问于 2018-02-10 22:03:35
回答 2查看 2.7K关注 0票数 7

我正在使用AVX固有的epi32()

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

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

当使用clang3.8 (或clang4)为avx2编译以下代码段时,它将使用生成代码并使用vpermd指令。

代码语言:javascript
运行
复制
#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,那么编译器就无法生成代码,并出现以下错误:

代码语言:javascript
运行
复制
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);
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

我对此有两个问题:

  1. 为什么clang可以用变量,gcc想要一个常量?
  2. 为什么gcc不能下决心?首先,它需要一个1位的即时值,然后它需要在0..3范围内的整数常量,这些是不同的东西。

顺便说一句,Intrinsics没有为_mm256_extract_epi32()的索引值指定约束,那么gcc还是clang,谁在这里呢?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-02-10 22:45:54

显然,GCC和Clang做出了不同的选择。国际水文学组织GCC做出了正确的选择,没有为可变指数实施这一政策。内部_mm256_extract_epi32不能转换为一条指令。如果在性能关键循环中使用变量索引,则此内在特性可能导致效率低下的代码。

例如,Clang3.8需要4个指令来实现带有变量索引的_mm256_extract_epi32。GCC强迫程序员考虑更有效的代码,以避免使用可变索引的_mm256_extract_epi32

然而,有时有一个可移植的(gcc,clang,icc)函数是有用的,它通过变量索引来模拟_mm256_extract_epi32

代码语言:javascript
运行
复制
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));
}    

这应该编译成内联后的三个指令:两个vmovds和一个vpermd (gcc 8.2与-m64 -march=skylake -O3):

代码语言:javascript
运行
复制
mm256_extract_epi32_var_indx:
  vmovd xmm1, edi
  vpermd ymm0, ymm1, ymm0
  vmovd eax, xmm0
  vzeroupper
  ret

注意,本质指南描述索引>=8的结果为0(无论如何这是一个不寻常的情况)。使用Clang3.8和mm256_extract_epi32_var_indx,索引的模块数减少了8。换句话说:只使用索引中最不重要的3位。请注意,Clang5.0的内存往返也不是很有效,请参阅这个该死的链接。Clang7.0无法用变量索引编译_mm256_extract_epi32

作为@Peter Cordes评论:使用固定的索引0、1、2或3,只需要一个pextrd指令就可以从xmm寄存器中提取整数。对于固定的索引4、5、6或7,需要两个指令。不幸的是,不存在在256位ymm寄存器上工作的vpextrd指令.

下一个例子说明了我的答案:

从SIMD本质开始的天真程序员可能会编写以下代码,将来自j<8的元素0、1、.、j-1和来自vec的元素相加。

代码语言:javascript
运行
复制
#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;
}

使用Clang3.8,这将编译成大约50 使用说明的分支和循环。GCC没有编译这段代码。显然,将这些元素相加的有效代码可能基于:

  1. 屏蔽元素j,j+1,.,7,和
  2. 计算水平和。
票数 7
EN

Stack Overflow用户

发布于 2018-02-11 02:02:28

它说必须是1位即时的__N不是_mm256_extract_epi32的第二位arg,它是作为arg到__builtin_ia32_vextractf128_si256 (大概是第3位)的一些功能。然后,它需要一个vpextrd的0..3范围内的整数常量,总共给出3位索引。

_mm256_extract_epi32是一个复合的内在特性,不是直接用单个builtin函数来定义的。

vpextrd r32, ymm, imm8不存在,只有xmm版本存在,因此_mm256_extract_epi32vextracti/f128 / vpextrd的包装器。Gcc选择只为编译时常量工作,所以它总是编译到最多2条指令。

如果需要运行时变量向量索引,则需要使用不同的语法;例如存储到数组并加载标量,并希望gcc将其优化为洗牌/提取。

或者定义具有正确元素宽度的GNU本机向量类型,并使用foo[i]像数组一样对其进行索引。

代码语言:javascript
运行
复制
typedef int v8si __attribute__ ((vector_size (32)));
v8si tmp = foo;   // may need a cast to convert from __m256i
int element_i = tmp[i];

gcc/clang中的__m256i定义为long long元素的向量,所以如果直接用[]索引它,就会得到qword元素。(而且您的代码不会用MSVC编译,因为MSVC根本就没有那样定义__m256i。)

最近我还没有检查asm中的任何一个:如果您关心效率,您可能需要使用运行时变量索引手动设计洗牌,就像@Wim的回答表明clang是这样的。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48726032

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档