为啥 C++ 标准库中没有 SIMD 功能?
Posted
技术标签:
【中文标题】为啥 C++ 标准库中没有 SIMD 功能?【英文标题】:Why is there no SIMD functionality in the C++ standard library?为什么 C++ 标准库中没有 SIMD 功能? 【发布时间】:2019-12-17 12:03:39 【问题描述】:SSE 自 1999 年以来一直存在,它及其后续扩展是提高 C++ 程序性能的最强大工具之一。然而,没有明确使用这一点的标准化容器/算法等(我知道?)。是否有一个原因?有没有没有通过的提案?
【问题讨论】:
你知道std::execution
policies吗? <algorithm>
库中的大多数算法都可以使用它们。
不是多线程的
SSE 内在函数是特定于 x86 的,而 C++ 标准在很大程度上是跨平台可移植的。在大多数人的手中,跨平台 SIMD 库可能会比自动矢量化做得更差,而能够有效使用手动 SIMD 的人可能会将其传递给低级 intel 内在函数。
是的,我知道隐式矢量化,但这永远无法保证,并且在您的程序中某处更改 1 行代码可能会阻止编译器对任何内容进行矢量化
嘿,怎么样,看起来他们在烤东西:en.cppreference.com/w/cpp/experimental/simd
【参考方案1】:
(parallelism TS v2) 中对显式短向量 SIMD 类型提供实验性支持,这些类型映射到常见 ISA 的 SIMD 扩展,但截至 2021 年 8 月只有 GCC 实现它。上面链接的 Cppreference 文档不完整,但还有其他Working Draft, Technical Specification for C++ Extensions for Parallelism, Document N4808 中涵盖的详细信息。该提案背后的想法是在博士项目 (2015 thesis here) 期间开发的。 GCC 实现wrote an article 的作者关于将现有的 SSE 字符串处理算法转换为使用其库的 2019 年迭代,实现类似的性能和更高的可读性。下面是一些使用它的简单代码和生成的程序集:
乘加
#include <experimental/simd> // Fails on MSVC 19 and others
using vec4f = std::experimental::fixed_size_simd<float,4>;
void madd(vec4f& out, const vec4f& a, const vec4f& b)
out += a * b;
使用-march=znver2 -Ofast -ffast-math
编译,我们确实得到了为此生成的硬件融合乘加:
madd(std::experimental::parallelism_v2::simd<float, std::experimental::parallelism_v2::simd_abi::_Fixed<4> >&, std::experimental::parallelism_v2::simd<float, std::experimental::parallelism_v2::simd_abi::_Fixed<4> > const&, std::experimental::parallelism_v2::simd<float, std::experimental::parallelism_v2::simd_abi::_Fixed<4> > const&):
vmovaps xmm0, XMMWORD PTR [rdx]
vmovaps xmm1, XMMWORD PTR [rdi]
vfmadd132ps xmm0, xmm1, XMMWORD PTR [rsi]
vmovaps XMMWORD PTR [rdi], xmm0
ret
点积
一个点/内积可以写得很简洁:
float dot_product(const vec4f a, const vec4f b)
return reduce(a * b);
-Ofast -ffast-math -march=znver2
:
dot_product(std::experimental::parallelism_v2::simd<float, std::experimental::parallelism_v2::simd_abi::_Fixed<4> >, std::experimental::parallelism_v2::simd<float, std::experimental::parallelism_v2::simd_abi::_Fixed<4> >):
vmovaps xmm1, XMMWORD PTR [rsi]
vmulps xmm1, xmm1, XMMWORD PTR [rdi]
vpermilps xmm0, xmm1, 27
vaddps xmm0, xmm0, xmm1
vpermilpd xmm1, xmm0, 3
vaddps xmm0, xmm0, xmm1
ret
(Godbolt link with some more playing around)。
【讨论】:
有趣。gcc -O2
不包括自动矢量化,因此使用 SIMD 可以证明它是用 __attribute__((vector_size(16)))
定义的。但是,godbolt.org/z/79KddEdba 表明,即使您在 C 中按值传递,它 not 也不会像 x86-64 System V ABI 中的普通 __m128
非类 typedef 那样在 xmm 寄存器中传递, 甚至通过指针返回它 :( 幸运的是,在内联后这并不重要,但您通常不想通过引用传递 SIMD 向量。
@PeterCordes 代码包含注释“以下确保函数参数通过堆栈传递。这对于跨 TU 边界的 ABI 兼容性很重要”:-(
这里更多地提到了 ODR、TU、内联问题(“大多数函数都标记为 [[gnu::always_inline]]
以启用具有不同 -m
标志的 TU 的准 ODR 一致性链接。 "): gcc.1065356.n8.nabble.com/…
"> > 请注意,过度使用 always_inline 会导致编译时问题 > >(参见 PR99785 示例)。 > > 啊,我应该验证这是否也是我的 stdx::simd 的原因 >实现编译起来很慢。” gcc.1065356.n8.nabble.com/…以上是关于为啥 C++ 标准库中没有 SIMD 功能?的主要内容,如果未能解决你的问题,请参考以下文章