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