8

当在 Rust 中编写将运行数百万次的整数函数时(想想像素处理),使用性能最高的操作很有用 - 类似于 C/C++。

虽然参考手册解释了行为的变化,但并不总是清楚哪些方法比标准(见注 1.)整数算术运算具有更高的性能。我假设wrapping_add编译成相当于 C 的加法的东西。

在标准操作(加/减/乘/模/除/移位/位操作...)中,哪些操作具有更高性能的替代方案,默认情况下不使用?


笔记:

  1. 按照标准 ,我的意思是使用符号或...等的整数算术a + b。编写数学表达式时您将使用什么 - 除非您特别需要使用包装或返回溢出的方法之一。i / kc % e
  2. 我意识到回答这个问题可能需要一些研究。所以我很高兴通过查看生成的程序集来进行一些检查,以查看哪些操作正在使用未经检查/原始操作。
  3. 可能是检查/未检查操作之间的速度差异并不显着,如果是这种情况,我仍然希望能够编写一个函数的“快速”版本来与“安全”版本进行比较,来我自己的结论是它是否是给定功能的合理选择。
  4. 在提到像素处理之后,SIMD 已经成为一种可能的解决方案。尽管这是一个很好的建议。这仍然给我们留下了无法使用 SIMD 优化的情况,因此仍然需要考虑快速整数运算的一般情况。
4

3 回答 3

7

在标准操作(加/减/乘/模/除/移位/位操作...)中,哪些操作具有更高性能的替代方案,默认情况下不使用?

请注意,Rust 是为性能而设计的;因此,虽然在Debug中检查整数运算,但它们被定义为在Release中包装,除非您特别指示编译器。

因此,在具有默认选项的发布模式下,以下之间严格没有性能差异:

  • +wrapping_add
  • -wrapping_sub
  • *wrapping_mul
  • /wrapping_div
  • %wrapping_rem
  • <<wrapping_shl
  • >>wrapping_shr

因此,对于无符号整数,其性能严格类似于 C 或 C++;然而,对于有符号整数,优化器可能会产生不同的结果,因为有符号整数的下溢/溢出在 C 和 C++ 中是未定义的行为(gcc 和 Clang 接受一个-fwrapv标志来强制包装,即使对于有符号整数,但这不是默认值)。

我希望使用checked_*,overflow_*saturating_*方法通常会更慢。


因此,一个有趣的切线是了解当您拨动开关并明确要求检查算术时会发生什么。

目前,Rust 实现1是下溢/溢出检查的精确实现。每个加法,减法,乘法,......都是独立检查的,优化器不擅长融合这些分支。

具体来说,精确的实现会排除临时溢出:5 + x - 5不能优化为x,因为5 + x可能会溢出。它还排除了一般的自动矢量化。

只有当优化器可以证明不存在溢出(它通常不能)时,您才可能希望重新获得更适合优化的无分支路径。

应该注意的是,在一般软件上,影响几乎不明显,因为算术指令只占总成本的一小部分。然而,当这个比例上升时,它可能会非常明显,并且确实出现在使用 Clang 的 SPEC2006 基准测试的一部分中。

这个开销足以被认为不适合默认激活的检查。

1 这是由于 LLVM 方面的技术限制;Rust 实现只是委托给 LLVM。


将来,希望可以使用模糊的检查实现。模糊实现背后的想法是,不是检查每个操作,而是执行它们并设置一个标志或在下溢/溢出的情况下毒化这些值。然后,在使用结果之前,执行检查(分支)。

根据 Joe Duffy 的说法,他们在 Midori 中进行了这样的实现,并且对性能的影响几乎看不到,所以这似乎是可行的。不过,我还不知道在 LLVM 中有任何类似的东西。

于 2016-12-12T14:42:00.117 回答
5

Rust 不保证其操作的速度。如果你想要保证,你需要调用汇编程序。

也就是说,目前 Rust 转发到 LLVM,所以你可以只调用内在函数,它将 1:1 映射到 LLVM 内在函数并使用这些保证。尽管如此,无论您做什么都不是 asm,请注意优化器可能对您认为最佳的内容有不同的看法,因此不会优化您对 LLVM 内在函数的手动调用。

也就是说,Rust 力求尽可能快,因此您可以假设(或简单地查看标准库的实现)具有相同功能的 LLVM 内在函数的所有操作都将映射到该 LLVM 内在函数,因此与LLVM 可以做到。

对于给定的基本算术运算,没有关于哪种运算最快的通用规则,因为它完全取决于您的用例。

于 2016-12-12T13:36:18.973 回答
3

想想像素处理

那么你根本不应该考虑单值操作;您想改用 SIMD 指令。这些目前在稳定的 Rust 中不可用,但有些可以通过特性门控函数访问,并且都可以通过汇编获得。

LLVM 是否有可能像 clang 一样将代码优化为 SIMD?

正如aochagavia 已经回答的那样,是的,LLVM 将自动矢量化某些类型的代码。但是,当您要求最高性能时,您通常不想让自己受优化器的影响。我倾向于希望在我的普通代码中实现自动向量化,然后为我的重数学内核编写直线代码,然后编写 SIMD 代码并测试正确性和速度基准。

于 2016-12-12T13:38:53.963 回答