C++标准库为什么没有SIMD功能?

10
自1999年起,SSE和其后续扩展已成为提高C++程序性能最强有力的工具之一。然而,就我所知,没有标准化的容器、算法等明确利用它们的(是这样吗?)。这是有原因的吗?是否存在过未通过的提案?

3
您是否了解std::execution策略?<algorithm>库中的大多数算法都可以使用它们。 - Yksisarvinen
6
这不是用于多线程吗? - Yamahari
8
SSE指令集是x86特定的,而C++标准在很大程度上可以跨平台使用。一个跨平台的SIMD库可能比自动向量化在大多数情况下表现更差,而能够有效使用手动SIMD的人可能会放弃它以使用低级别的英特尔指令。 - parktomatomi
2
是的,我知道隐式向量化,但这并不是保证的。在程序中更改某一行代码可能会阻止编译器对任何内容进行向量化。 - Yamahari
5
嘿,看起来他们正在烘焙什么东西:https://en.cppreference.com/w/cpp/experimental/simd - parktomatomi
显示剩余14条评论
1个回答

6
并行 TS v2中有对显式短向量 SIMD 类型的实验性支持,这些类型映射到常见 ISA 的 SIMD 扩展,但截至 2021 年 8 月只有 GCC 实现了它。上面链接的 Cppreference 文档不完整,但在《C++ 并行扩展技术规范工作草案》文件 N4808中还有其他细节。该提案的思想是在2015 年的博士论文期间开发的。GCC 实现的作者写了一篇文章,介绍如何将现有的 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链接).


1
有趣。gcc -O2不包括自动向量化,因此使用SIMD的证明是它用__attribute__((vector_size(16)))定义。但是,https://godbolt.org/z/79KddEdba表明,即使在C中按值传递,它也不会像普通的`__m128`非类typedef那样通过xmm寄存器传递,在x86-64 System V ABI中,甚至返回它需要使用指针:(幸运的是,在内联之后这并不重要,但通常不想通过引用传递SIMD向量。 - Peter Cordes
2
@PeterCordes 代码中包含一条注释:“以下内容确保函数参数通过堆栈传递。这对于跨TU边界的ABI兼容性非常重要” :-( - Marc Glisse
这里更多地提到了ODR、TU、inline问题(“大多数函数都标记为[[gnu::always_inline]],以启用具有不同-m标志的TUs的准ODR兼容链接。”):http://gcc.1065356.n8.nabble.com/PATCH-1-2-Add-std-experimental-simd-from-the-Parallelism-TS-2-td1807958.html - ahcox
请注意,过度使用always_inline可能会导致编译时出现问题(例如PR99785)。啊,我应该验证一下这是否也是我的stdx::simd实现编译缓慢的原因。 - ahcox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接