手动 SIMD 代码的可负担性 [关闭]

Posted

技术标签:

【中文标题】手动 SIMD 代码的可负担性 [关闭]【英文标题】:Manual SIMD code affordability [closed] 【发布时间】:2017-06-27 06:08:48 【问题描述】:

我们正在运行一个计算密集型的项目,现在我们让编译器进行 SSE 优化。但是,我们不确定我们是否获得了代码的最佳性能。

我的问题是,我理解,很宽泛,但我没有找到很多关于此的建议:编写手动 SIMD 代码负担得起,或者换句话说,值得付出努力吗?

在这里,可负担性是指对成本收益的粗略估计,例如speedup / development_time,或在项目开发环境中合理的任何其他衡量标准。

缩小范围:

我们分析了代码,我们知道计算是最繁重的部分 我们有一个可以轻松使用 Boost.SIMD 和类似库的 C++ 代码 负担能力不应影响代码可读性,我们假设我们对 SSE/AVX 充满信心 代码当前是多线程的 (OpenMP) 我们的编译器是英特尔的icc

【问题讨论】:

“我的问题是,我理解,广泛的......” 那么你为什么要问你是否已经知道它是题外话 您是否考虑过并行化(例如线程)?也许只是手动优化代码的部分?在您实际尝试某事并将结果与​​某个基线进行比较之前,这对任何人来说都很难,所以请说出具体的内容。 您可能应该指定您的编译器。我知道英特尔认为矢量化是 ICC 的主要优势。另一方面,当我尝试 GCC 时,它得到的支持很少,而且相当过时。一般来说,我认为编译器在应用 SIMD 的能力方面比在非平凡场景中的经验丰富的程序员更有限。不知道这是否适用于您的情况。 “负担能力”一词本身也很宽泛。你的意思是在工时?感知的或实际的加速?还有什么?或者可能是一个组合? 我尽可能多地澄清了您的疑虑。 @Someprogrammerdude 除了我们现在拥有的代码之外,我们没有可比较的基线。由于这是一个设计决策,我们必须进行一些我们不熟悉的成本/收益分析(例如,了解实际进行此 SIMD 移植的其他项目)。 【参考方案1】:

非常同意 Paul R,只是想补充一点,在大多数情况下,IMO 内在函数/asm 优化不值得付出努力。在大多数情况下,这些优化是营销驱动的,即我们在特定平台上榨取性能只是为了(在大多数情况下)获得更好的数字。

如今,仅在 asm 中重写 C/C++ 代码几乎不可能获得一个数量级的性能。正如 Paul 所指出的,在大多数情况下,这是内存/缓存访问和方法/算法(即并行化)的问题。

您应该尝试的第一件事是使用硬件性能计数器(使用免费的“性能”工具或英特尔 VTune)分析您的代码并了解真正的瓶颈。例如,计算期间的内存访问实际上是最常见的瓶颈,而不是计算本身。所以这种代码的手动向量化没有帮助,因为 CPU 无论如何都会在内存上停止。

这样的分析总是值得的,因为您更好地了解您的代码和 CPU 架构。

您应该尝试的下一件事是优化您的代码。有多种方法:优化数据结构、缓存友好的内存访问模式、更好的算法等。例如,您在结构中声明字段的顺序在某些情况下可能会对性能产生重大影响,因为您的结构可能有漏洞和占用两行缓存而不是一行。另一个例子是错误共享,当您在 CPU 之间 ping-pong 相同的缓存行时,简单的缓存对齐可能会给您带来一个数量级的更好性能。

这些优化总是值得付出努力的,因为它们也会影响您的低级代码。

那么你应该尝试帮助你的编译器。例如,默认情况下,编译器矢量化/展开内部循环,但矢量化/展开外部循环可能会更好。您可以使用#pragma 提示来执行此操作,有时值得付出努力。

您应该尝试的最后一件事是使用内在函数/asm 重写已经高度优化的 C/C++ 代码。这可能有一些原因,例如更好的指令交错(因此您的 CPU 管道总是很忙)或使用特殊的 CPU 指令(即用于加密)。合理的内在函数/asm 用法的实际数量可以忽略不计,而且它们总是依赖于平台。

因此,如果没有关于您的代码/算法的更多详细信息,很难猜测它是否适合您的情况,但我敢打赌没有。最好将精力花在分析和独立于平台的优化上。如果你真的需要这种计算能力,最好看看 OpenCL 或类似的框架。最后,投资于更好的 CPU:这种投资的效果是可预测且即时的。

【讨论】:

关于优化的自上而下性质的好点 - 首先正确设计和算法,然后专注于数据结构和缓存/内存使用,甚至在开始代码优化和低级别的东西之前SIMD。话虽如此,对于“前沿”性能,您需要执行上述所有操作,但在一般情况下,您可以少做很多(包括根本没有优化!)。 大幅加速当然是可能的。例如通过手动使用 AVX+FMA 的 C++ 内在函数,我在 Intel Skylake 上获得了 50 倍的加速,该函数检查了一个点数组是否位于多边形内部/外部。 (通常具有 4 到 8 个顶点的投影射线算法,用于约 1M 点的数组,x 和 y 坐标存储在单独的数组中。50 倍的加速比在同一个 Skylake CPU 上实现标量 C++。最初的矢量化版本比标量快 30 倍,并且调整源代码以获取 gcc 为 HSW/SKL 制作好的 asm 发现另外约 1.5 倍的加速。) 我不认为我可以用可移植的 C++ 编写它,以使 gcc 可以自动矢量化为同样好的代码。即使我可以,使用内在函数也更容易。 (使用 Agner Fog 的矢量类库,我能够对其进行模板化,因此我可以轻松制作一个与 AVX2 版本共享几乎所有代码的 AVX512 版本。)当然,我是性能调优专家,所以我知道什么是 asm我希望编译器制作。无论如何,因为指出内存带宽很重要而受到支持。对于多边形以外的东西(例如椭圆和矩形),内存是一个瓶颈。 @PeterCordes 感谢 cmets。只是几点说明:1)我同意在某些情况下显着加速是可能的。加密是 IMO 的一个很好的例子——在某些平台上有排列/哈希计算的说明。不幸的是,我们手头没有代码,所以这些只是猜测...... 2)与 GCC 相比,IMO ICC 在矢量化方面是一个不同的联盟,但它并不完美,总有改进的空间...... @PeterCordes 和 3) 从 AVX2 迁移到 AVX-512 是一回事,但从 Intel 迁移到 ARM 平台是另一回事。考虑到提高性能的所有可能性,人们可能会发现投资于每个拥有 100 多个内核的 ARM 系统(或农场)会更划算。只是一个例子,确定这一切都取决于任务......【参考方案2】:

您需要进行成本效益分析,例如如果你可以投入 X 个月的努力以 $Y 的成本让你的代码运行速度快 N 倍,这意味着要么降低硬件成本(例如,在 HPC 环境中减少 CPU),要么减少运行时间,这在某种程度上等同于成本效益,那么这是一个简单的算术练习。 (但请注意,存在一些无形的长期成本,例如 SIMD 优化代码往往更复杂、更容易出错、更不便携且更难维护。)

如果代码的性能关键部分(热门的 10%)是可矢量化的,那么您可能能够获得一个数量级的加速(双精度浮点数更少,更窄的数据类型(例如 16 位)定点)。

请注意,这种优化并不总是将标量代码转换为 SIMD 代码的简单问题 - 您可能需要考虑您的数据结构和缓存/内存访问模式。

【讨论】:

是的,成本效益,但实际上我们不知道如何估计这些因素。我们知道我们的计算部分花费了 50-70% 的时间,进行浮点(或双精度)操作。如果我们有一些以前的临床病例,我们可以有所了解,但我们没有找到关于这些问题的任何来源。 好吧,如果您可以为您的问题添加更多细节,我也许可以给您一些挥手的估计。理想情况下,您需要显示一个执行配置文件(使用例如 Linux 上的 perf 收集)和一些对性能最关键的部分的代码。您可能还需要考虑聘请顾问来帮助您评估项目/提供指导/实施 SIMD 代码(在这种情况下,我可以通过 LinkedIn 联系到。;-))。

以上是关于手动 SIMD 代码的可负担性 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

关闭上下文管理器后保持适合文件的可访问性

在组件外部使用 redux 钩子来实现代码的可重用性

代码的可移植可复用性

软件的可复用性和维护性

使用 ARM SIMD 指令优化掩码功能

是否可以使用SIMD指令批处理相同的功能?