Rust执行类型转换在运行时或在编译静态?



在使用C或其他编程语言时,我总是遇到无法避免类型转换的情况,例如let c_var = rust_var as u32,我觉得我担心性能。

类型转换是在运行时还是编译时执行?在线使用as性能不高吗?

类型转换是在运行时还是编译时执行的?使用as内联不是那么高效吗?

通常取决于是否有表示变化。如果有,则必须有一个运行时组件来强制转换,例如integer ->整数,整数->Float, Float ->整数。

然而,这些转换通常是硬件操作,甚至可能是硬件无操作,例如,如果你将u16转换为u32,在x64上,编译器将只存储u16并从同一个寄存器读取u32,可能使用带有零扩展的move(取决于寄存器的语义)。

如果你担心,你可以看看godbolt的输出来获得一些提示或证据。例如:

pub fn conv(num: u16) -> u32 {
num as _
}
pub fn conv2(num: u32) -> u64 {
num as _
}
example::conv:
movzx   eax, di
ret
example::conv2:
mov     eax, edi
ret

因为在x86上16位(和8位)寄存器不支持零扩展,而32位寄存器支持:

  • 32位操作数生成32位结果,在目的通用寄存器中0扩展为64位结果。
  • 8位和16位操作数生成8位或16位结果。目的通用寄存器的上56位或48位(分别)不会被该操作修改。如果8位或16位操作的结果用于64位地址计算,则显式地将寄存器符号扩展到完整的64位。

如果数据"流动"正确,尽管在运行时发生(因为它匹配ISA的默认行为),但强制转换是运行时无操作,如果不匹配,则可能需要进行显式操作,例如u16 ->ARM64上的u32需要显式的上16位归零:

example::conv:
and     w0, w0, #0xffff
ret

因为ARM从一开始就是32b架构,但是对于u32来说这不是问题。u64:

example::conv2:
mov     w0, w0
ret

因为与x86不同,它是零扩展的。

最新更新