gpt4 book ai didi

c++ - Constexpr 和 SSE 内在函数

转载 作者:可可西里 更新时间:2023-11-01 17:58:10 24 4
gpt4 key购买 nike

大多数 C++ 编译器支持 SIMD(SSE/AVX) 指令,其内部结构如

_mm_cmpeq_epi32

我的问题是这个函数没有被标记为constexpr,虽然“语义上”没有理由让这个函数不是constexpr,因为它是一个纯函数。

有什么方法可以编写我自己的(例如)_mm_cmpeq_epi32 版本,即 constexpr

显然我希望该函数在运行时使用适当的 asm,我知道我可以使用 constexpr 的慢函数重新实现任何 SIMD 函数。

如果您想知道我为什么关心 SIMD 函数的 constexpr。非 constexprness 具有传染性,这意味着我的任何使用这些 SIMD 函数的函数都不能是 constexpr

最佳答案

很遗憾,Intel 的内在函数未定义为 constexpr

没有理由他们做不到;编译器可以并且确实在编译时评估它们以进行常量传播和其他优化。 (这是内置函数/内在函数优于单指令内联 asm 包装器的主要原因之一。)


GCC 解决方案。 (不适用于 clang 或 MSVC)。

ICC 会编译它,但当您尝试将它用作 constexpr __m128i 的初始化程序的一部分时会阻塞。

constexpr
__m128i pcmpeqd(__m128i a, __m128i b) {
return (v4si)a == (v4si)b; // fine with gcc and ICC

//return (__m128i)__builtin_ia32_pcmpeqd128((v4si)a, (v4si)b); // bad with ICC
//return _mm_cmpeq_epi32(a,b); // not constexpr-compatible
}

See it on the Godbolt compiler explorer ,有两个测试调用者(一个有变量,一个有
constexpr __m128i v1 {0x100000000, 0x300000002}; 输入)。有趣的是,ICC 通过pcmpeqd_mm_cmpeq_epi32 进行持续传播;它加载两个常量并使用和实际 pcmpeqd,即使启用了优化。使用/不使用 constexpr 都会发生同样的事情。我认为它通常会优化

gcc 接受constexpr __m128i vector_const { pcmpeqd(__m128i{0,0}, __m128i{-1,-1}) };


GCC(但不是 clang)将 __builtin_ia32 函数视为 constexpr 兼容。 documentation for GNU C x86 built-in functions没有提到这一点,但可能只是因为它是 C 文档,而不是 C++。

GNU C native vector 语法也是 constexpr 兼容的;这是第二个选项,只有在您不关心 MSVC 时才可行。

GNU C 将 __m128i 定义为两个 long long 元素的 vector 。所以对于整型SIMD,需要定义其他类型(或者使用gcc/clang/ICC的immintrin.h

定义的类型

(唯一奇怪的是 static const __m128i foo = _mm_set1_epi32(2); 不会变成常量初始值设定项;它在运行时从 .rodata 复制,因此很糟糕,使用在每次函数调用时检查变量以查看变量是否需要静态初始化的保护变量。)


GCC 的 xmmintrin.hemmintrin.h 根据 native vector 运算符(如 *)或 __builtin_ia32 定义英特尔内部函数 功能。看起来他们更喜欢尽可能使用运算符,而不是 (__m128i)__builtin_ia32_pcmpeqd128((v4si)a, (v4si)b);

gcc 确实需要在不同 vector 类型之间进行显式转换。

来自 gcc7.3 的 emmintrin.h (SSE2):

extern __inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_cmpeq_epi32 (__m128i __A, __m128i __B)
{
return (__m128i) ((__v4si)__A == (__v4si)__B);
}

#ifdef __OPTIMIZE__
extern __inline __m128i __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_shuffle_epi32 (__m128i __A, const int __mask)
{
return (__m128i)__builtin_ia32_pshufd ((__v4si)__A, __mask);
}
#else
#define _mm_shuffle_epi32(A, N) \
((__m128i)__builtin_ia32_pshufd ((__v4si)(__m128i)(A), (int)(N)))
#endif

有趣的是:如果在禁用优化的情况下编译,gcc 的头文件在某些​​情况下会避免使用内联函数。我想这会导致更好的调试符号,所以你不会单步进入内联函数的定义(当在 GDB 中使用 stepi 优化代码并显示 TUI 源窗口时确实会发生这种情况。 )

关于c++ - Constexpr 和 SSE 内在函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51880079/

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