Constexpr and SSE intrinsics
Constexpr and SSE intrinsics
大多数C++编译器都支持具有类似内部功能的 SIMD(SSE/AVX( 指令
_mm_cmpeq_epi32
我的问题是这个函数没有标记为constexpr
,尽管"语义上"没有理由不constexpr
这个函数,因为它是一个纯函数。
有什么方法可以编写我自己的版本(例如(constexpr
_mm_cmpeq_epi32
?
显然,我希望运行时的函数使用正确的asm,我知道我可以重新实现任何具有constexpr
慢函数的SIMD函数。
如果您想知道我为什么关心 SIMDconstexpr
功能。非连续性具有传染性,这意味着我使用这些 SIMD 函数的任何函数都不能constexpr
。
不幸的是,英特尔的内在函数没有被定义为constexpr
。
没有理由它们不能这样做;编译器可以在编译时评估它们以进行常量传播和其他优化。 (这是内置函数/内部函数比内联 asm 包装器更好的单个指令的一个主要原因。
海湾合作委员会的解决方案。 (不适用于 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
}
在 Godbolt 编译器资源管理器上看到它,有两个测试调用器(一个带有变量,一个带有constexpr __m128i v1 {0x100000000, 0x300000002};
输入(。 有趣的是,ICC不会通过pcmpeqd
或_mm_cmpeq_epi32
进行常量传播;它加载两个常量和用途和实际pcmpeqd
,即使启用了优化。 同样的事情发生在有/没有constexpr的情况下。我认为它通常会优化
海湾合作委员会确实接受constexpr __m128i vector_const { pcmpeqd(__m128i{0,0}, __m128i{-1,-1}) };
GCC(但不是 clang(将__builtin_ia32
函数视为constexpr
兼容。GNU C x86 内置函数的文档没有提到这一点,但可能只是因为它是 C 文档,而不是C++。
GNU C 原生矢量语法也是constexpr
兼容的;这是第二种选择,只有在你不关心 MSVC 的情况下才可行。
GNU C 将__m128i
定义为两个long long
元素的向量。 因此,对于整数 SIMD,您需要定义其他类型(或使用 gcc/clang/ICC 定义的类型immintrin.h
(唯一奇怪的是,static const __m128i foo = _mm_set1_epi32(2);
不会变成常量初始值设定项;它在运行时从.rodata
复制,因此很糟糕,使用一个保护变量,该变量在每次函数调用时都会被检查以查看变量是否需要静态初始化。
GCC 的xmmintrin.h
和emmintrin.h
根据原生向量运算符(如*
(或__builtin_ia32
函数来定义英特尔内部函数。 看起来他们更喜欢尽可能使用运算符,而不是(__m128i)__builtin_ia32_pcmpeqd128((v4si)a, (v4si)b);
GCC 确实需要不同载体类型之间的显式强制转换。
来自 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 的标头在某些情况下会避免内联函数。 我想这会导致更好的调试符号,因此您不会单步进入内联函数的定义(在显示 TUI 源窗口的优化代码中使用 GDB 中的stepi
时确实会发生这种情况。
现在在c++20 中有一个跨平台的解决方案。 std::is_constant_evaluated 允许我们做到这一点。
template<typename T>
constexpr auto add(T&& l, T&& r) noexcept
{
if (std::is_constant_evaluated())
slow_add(std::forward<T>(l), std::forward<T>(r));
else
_mm_add_pd(l.value, r.value);
}
请注意此处使用普通的 if 语句。如果 constexpr 使用很诱人,但这总是会导致函数返回 true。不用担心,分支将始终被优化出来,因为 std::is_constant_evaluated 的值在编译时总是已知的(即使它返回 false(。
- C++ SSE 内部函数:将结果存储在变量中
- LLVM 编译:目标的配方 '../lib/IR/Release+Asserts/Intrinsics.gen.tmp' failed
- 将内联程序集尾调用函数尾声替换为用于x86/x64 msvc的Intrinsics
- Constexpr and SSE intrinsics
- 如何使用SSE将__m128i注册乘以浮点因子?
- 使用SSE内部函数复制少量数据时出现问题
- 可以在 macOS 上启用的最低支持的 SSE 标志是什么?
- SSE 标志应该如何与现代 CMake 一起添加?
- 快速 SSE 射线 - 4 三角形交叉点
- 使用 SSE 以最快的速度缩小 8 位灰度图像
- 如何用SSE优化矩阵3乘3乘法与点?
- 使用双精度运算的快速 SSE 低精度指数
- 手动矢量化/SSE 用于 C++ 中的复杂问题
- 单精度矩阵运算的特征性能 AVX 与 SSE 没有区别?
- 编写 std::copysign 的可移植 SSE/AVX 版本
- SSE 内联汇编和可能的 g++ 优化错误
- SSE 整数 2^n 的 2 次方,对于没有 AVX2 的 32 位整数
- 带有SSE的有条件陈述
- SSE和iostream:浮点类型的错误输出
- SSE int64 Intrinsics