Rust 编译器没有优化 lzcnt? (和类似的功能)
Posted
技术标签:
【中文标题】Rust 编译器没有优化 lzcnt? (和类似的功能)【英文标题】:Rust compiler not optimising lzcnt? (and similar functions) 【发布时间】:2021-12-25 16:24:30 【问题描述】:做了什么:
这是在 Compiler Explorer 上进行实验的结果,以确定编译器 (rustc) 在涉及 log2()
/leading_zeros()
和类似函数时的行为。我发现这个结果似乎非常奇怪和令人担忧:
Compiler Explorer link
代码:
pub fn lzcnt0(val: u64) -> u64
val.leading_zeros() as u64
pub unsafe fn lzcnt1(val: u64) -> u64
core::arch::x86_64::_lzcnt_u64(val)
pub unsafe fn lzcnt2(val: u64) -> u64
asm_lzcnt(val)
#[inline]
pub unsafe fn asm_lzcnt(val: u64) -> u64
let lzcnt: u64;
core::arch::asm!("lzcnt , ", in(reg) val, lateout(reg) lzcnt, options(nomem, nostack));
lzcnt
输出:
example::lzcnt0:
test rdi, rdi
je .LBB0_2
bsr rax, rdi
xor rax, 63
ret
.LBB0_2:
mov eax, 64
ret
example::lzcnt1:
jmp core::core_arch::x86_64::abm::_lzcnt_u64
core::core_arch::x86_64::abm::_lzcnt_u64:
lzcnt rax, rdi
ret
example::lzcnt2:
lzcnt rdi, rax
ret
编译器选项是为了最好地模拟 cargo 的“发布”配置(最好使用 opt-level=3),否则我会尽力让编译器优化功能。具体目标应该没关系,只要是针对x86-64的,我试过x86_64-pc-windows-msvc,gnu,unknown-linux-gnu
。
预期结果:
所有这些输出都应该与lzcnt2
相同。 Instruction Performance Tables lzcnt
显然是一条快速指令,应该使用,在如此低级的函数中拥有不必要的分支是令人沮丧的。更奇怪的是,函数_lzcnt_u64()
在后台调用leading_zeros()
- 编译器很乐意将其魔术掉(也没有检查或断言),但似乎不会为底层函数执行此操作。更重要的是,即使在这种情况下,编译器也不会内联lzcnt
指令? (实现也将函数标记为#[inline]
)当然,jmp
并没有那么糟糕,但它完全没有必要,应该避免。
可能是什么:
编译器错误? 我不明白的有目的的选择? 我不明白如何正确使用 Compiler Explorer? 其他?我在 log2
和(我认为)其他函数中看到了类似的结果,这些函数在其实现中依赖于 ctlz
rust 编译器。
如果您充分了解编译器,我们将不胜感激。我不喜欢编写大量实用函数,但如果没有更好的选择,我会这样做。
附:如果您的答案是在大多数情况下性能提升可以忽略不计,和/或由于代码质量或类似原因我不应该关心:我理解这种情绪,但这不是这个问题的重点。我正在为个人项目中的裸机热代码编写代码。
【问题讨论】:
【参考方案1】:旧的 x86-64 CPU 不支持lzcnt
,因此默认情况下 rustc/llvm 不会发出它。 (他们会以bsr
执行它,但行为并不相同。)
使用-C target-feature=+lzcnt
启用它。 Try.
更一般地说,您可能希望使用-C target-cpu=XXX
来启用特定 CPU 型号的所有功能。使用rustc --print target-cpus
作为列表。
特别是,-C target-cpu=native
将为运行 rustc 本身的 CPU 生成代码,例如如果您将在编译它的同一台机器上运行代码。
【讨论】:
非常感谢,我会给target-feature
s 看看——正是我需要的。我会尽快接受你的回答。以上是关于Rust 编译器没有优化 lzcnt? (和类似的功能)的主要内容,如果未能解决你的问题,请参考以下文章