我需要执行128位的64位师在锈。x86-64 ISA包含用于此目的的本机DIV指令。但是,我编译的测试代码不使用此指令。
测试代码:
pub fn div(hi: u64, lo: u64, divisor: u64) -> u64 {
assert!(hi < divisor);
let dividend = ((hi as u128) << 64) + lo as u128;
(dividend / divisor as u128) as u64
}编译器资源管理器输出:
example::div:
push rax
cmp rdi, rdx
jae .LBB0_1
mov rax, rdi
mov rdi, rsi
mov rsi, rax
xor ecx, ecx
call qword ptr [rip + __udivti3@GOTPCREL]
pop rcx
ret
.LBB0_1:
...相反,通过__udivti3执行128位除法的低效128位。这可能是因为如果商数不适合64位,则DIV指令会导致CPU异常。然而,就我的情况而言,这是不可能的: hi <除数,lo < 2^64 ->股利= hi * 2^64 + lo <= (除数- 1) * 2^64 + 2^64 -1=除数* 2^64 -1 ->股息/除数<= 2^64 -1/除数< 2^64
如何强制编译器使用本机指令?
发布于 2022-09-09 23:25:50
您唯一的选择是使用内联程序集。可能有一个模糊的编译器标志组合,可以强迫llvm执行优化本身,但我不认为试图找到它是值得的。对于程序集,它将如下所示:
use std::arch::asm;
pub fn div(hi: u64, lo: u64, divisor: u64) -> u64 {
assert!(hi < divisor);
#[cfg(target_arch = "x86_64")]
unsafe {
let mut quot = lo;
let mut _rem = hi;
asm!(
"div {divisor}",
divisor = in(reg) divisor,
inout("rax") quot,
inout("rdx") _rem,
options(pure, nomem, nostack)
);
quot
}
#[cfg(not(target_arch = "x86_64"))]
{
let dividend = ((hi as u128) << 64) + lo as u128;
(dividend / divisor as u128) as u64
}
}在x86_64上,这只是将除法编译成一个小寄存器洗牌,然后是一个div,然后在其他系统上执行对__udivti3的调用。它也不应该妨碍优化器太多,因为它是纯的。
确实值得对您的应用程序进行基准测试,看看这是否真的有帮助。对于llvm来说,对整数除法的推理要比内联程序集容易得多,其他地方的优化也很容易导致这个版本比使用默认版本运行得慢。
https://stackoverflow.com/questions/73667228
复制相似问题