哪个整数操作在Rust中具有更高性能的替代方法?

ide*_*n42 8 micro-optimization rust llvm-codegen

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

虽然参考手册解释了行为的变化,但并不总是清楚哪种方法的性能高于标准(参见注释1)整数算术运算.我假设wrapping_add编译成等同于C的补充.

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


注意:

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

Mat*_* M. 6

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

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

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

  • +wrapping_add
  • -wrapping_sub
  • *wrapping_mul
  • /wrapping_div
  • TODO:检查模数,移位,否定和绝对

对于无符号整数,性能因此严格地类似于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中有任何类似的努力.


oli*_*obk 5

Rust不保证其运营速度.如果您需要保证,则需要调用汇编程序.

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

也就是说,Rust努力尽可能快,因此您可以假设(或者只是查看标准库的实现)所有具有相同LLVM内在函数的操作将映射到该LLVM内在函数,因此可以像LLVM可以做到这一点.

对于给定的基本算术运算,哪个操作最快是没有一般规则的,因为它完全取决于您的用例.