在Rust中编写将运行数百万次的整数函数(想想像素处理)时,使用性能最高的操作很有用 - 类似于C / C ++。
虽然参考手册解释了行为的变化,但并不总是清楚哪种方法的性能高于标准(参见注释1。)整数算术运算。我假设wrapping_add
编译成等同于C的补充。
在标准操作中(加/减/乘/模/除/移位/位操作...),哪些操作具有更高性能的替代方法,默认情况下不使用?
注意:
a + b
,i / k
或c % e
等等的整数算术。答案 0 :(得分:6)
在标准操作(加/减/乘/模/除/移位/位操作...)中,哪些操作具有更高性能的替代方法,默认情况下不使用?
请注意,Rust是为性能而设计的;因此,在 Debug 中检查整数操作时,它们被定义为发布中的 wrap ,除非您特别指示编译器。
因此,在使用默认选项的发布模式下,严格之间没有性能差异:
+
和wrapping_add
-
和wrapping_sub
*
和wrapping_mul
/
和wrapping_div
对于无符号整数,性能严格地类似于C或C ++;但是,对于有符号整数,优化器可能会产生不同的结果,因为有符号整数的下溢/溢出是C和C ++中的未定义行为(gcc和Clang接受-fwrapv
标志,即使对于有符号整数也要求包装,但它&#39 ; s不是默认值。)
我希望使用checked_*
,overflow_*
和saturating_*
方法一般会慢一点。
然后,有趣的切线是了解当您翻转开关并明确要求检查算术时会发生什么。
目前,Rust实现 1 是下溢/溢出检查的精确实现。每个加法,减法,乘法,...都是独立检查的,优化器不擅长融合这些分支。
具体来说,精确实现会排除临时溢出:5 + x - 5
无法优化为x
,因为5 + x
可能会溢出。它也排除了一般的自动矢量化。
只有当优化器能够证明没有溢出时(通常它不能),您可能希望重新获得更易于优化的无分支路径。
应该注意的是,在通用软件上,影响几乎不可察觉,因为算术指令只代表总成本的一小部分。然而,当这个比例上升时,它可能非常明显,实际上它出现在与Clang的SPEC2006基准测试的一部分中。
此开销足以被视为不适合默认激活支票。
1 这是由于LLVM方面的技术限制; Rust实现只委托给LLVM。
将来,希望检查的模糊实现可用。模糊实现背后的想法是,不是检查每个操作,而是执行它们并且设置标志或者在下溢/溢出的情况下中断值。然后,在使用结果之前,执行检查(分支)。
根据Joe Duffy的说法,他们在Midori中有这样的实现,性能影响几乎不可察觉,所以它似乎是可行的。但是,我还没有意识到在LLVM中有任何类似的东西。
答案 1 :(得分:5)
Rust不保证其运营速度。如果您需要保证,则需要调用汇编程序。
也就是说,当前Rust转发到LLVM,所以你可以调用内在函数,它将1:1映射到LLVM内在函数并使用这些保证。但是,无论你做什么都不是asm,请注意优化器可能对你认为最优的东西有不同的看法,因此不优化你对LLVM内在函数的手动调用。
也就是说,Rust努力尽可能快,所以你可以假设(或者只是看标准库的实现)所有具有相同LLVM内在函数的操作都将映射到该LLVM内在函数,因此和LLVM一样快。
对于给定的基本算术运算,哪个操作最快是没有一般规则的,因为它完全取决于您的用例。
答案 2 :(得分:3)
想像素处理
那么你根本不应该考虑单值操作;您想要使用SIMD指令。这些目前在稳定的Rust中不可用,但有些可通过功能门控功能访问,并且所有功能都可通过汇编获得。
LLVM是否有可能将代码优化为SIMD,就像它对clang一样?
作为aochagavia already replied,是的,LLVM将自动向量化某些类型的代码。但是,当您要求最高性能时,您通常不希望自己处于优化器的一时兴起。我倾向于希望在我的普通普通代码中进行自动向量化,然后为我的重数学内核编写直线代码,然后编写SIMD代码并测试速度的正确性和基准。