Rust 是在运行时还是在编译中静态执行类型转换?

Do Rust perform type casting in runtime or in compilation statically?

提问人:xc wang 提问时间:4/9/2023 最后编辑:Chayim Friedmanxc wang 更新时间:4/10/2023 访问量:150

问:

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

类型转换是在运行时还是在编译中执行?使用内联不是那么有性能吗?as

防锈 铸件

评论


答:

5赞 Masklinn 4/9/2023 #1

类型转换是在运行时还是在编译中执行?作为内联使用不是那么有性能吗?

通常取决于是否存在表示形式更改。如果有,那么强制转换必然有一个运行时组件,例如整数 -> 整数、整数 -> 浮点数、浮点数 ->整数。

然而,这些转换通常是硬件操作,甚至可能是硬件空操作,例如,如果将 u16 转换为 u32,在 x64 上,编译器将只存储 u16 并从同一寄存器中读取 u32,可能使用 mov-with-zero-extend(取决于寄存器的语义)。

如果你担心,你可以看看 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 位结果,零扩展为 64 位结果。
  • 8 位和 16 位操作数生成 8 位或 16 位结果。目标通用寄存器的上 56 位或 48 位(分别)不会作修改。如果 8 位或 16 位运算的结果用于 64 位地址计算,则将寄存器显式符号扩展为完整的 64 位。

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

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

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

example::conv2:
        mov     w0, w0
        ret

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