为什么Rust编译器在移动一个不可变的值时要执行复制操作?

utugiqy6  于 2023-02-19  发布在  其他
关注(0)|答案(2)|浏览(89)

我对移动和复制的直觉一定是错的。我希望Rust编译器优化一个不可变值的移动作为一个no-op。因为这个值是不可变的,我们可以在移动后安全地重用它。但是Rust 1.65.0 on Godbolt编译到汇编,将这个值复制到内存中的一个新位置。我正在研究的Rust代码:

pub fn f_int() {
    let x = 3;
    let y = x;
    println!("{}, {}", x, y);
}

生成的-C opt-level=3部件:

; pub fn f_int() {
    sub     rsp, 88
    ; let x = 3;
    mov     dword ptr [rsp], 3
    ; let y = x;
    mov     dword ptr [rsp + 4], 3
    mov     rax, rsp
    ...

为什么let y = x;会导致mov dword ptr [rsp + 4], 3mov rax, rsp?为什么编译器不将y视为程序集中x的同一个变量?
This question看起来很相似,但它是关于不是复制的字符串的。我的问题是关于复制的整数的。看起来我所描述的不是错过的优化机会,而是我理解上的根本错误。)

nbysray5

nbysray51#

如果你像这样改变你的例子

pub fn f_int() -> i32 {
    let x = 3;
    let y = x;
    // println!("{}, {}", x, y);
    x+y
}

进行优化

example::f_int:
        mov     eax, 6
        ret

宏(以及write!() ...)获取其参数上的引用,并使用这些引用提供格式化机制。(没有内联)需要数据存储在内存中的某个地方才能有地址。因为类型是Copy,语义暗示我们具有两个不同的存储器,否则,共享存储器将是对 * 移动 * 操作(而不是 * 复制 *)的优化。

qq24tv8q

qq24tv8q2#

我不认为这是你理解中的一个根本错误,但这里有一些有趣的观察。
首先,由于println!()的设计,它(尤其是格式化机制)很难优化,所以println!()没有优化也就不足为奇了。
第二个是it is generally not obvious it is OK to perform this optimization, because it observably make the addresses equivalentprintln!()获取打印值的地址(并将其传递给一个opaque函数)。事实上,Copy类型比非Copy类型更难在这方面进行调整,因为对于Copy类型,原始变量在移动后仍然可以使用,而对于非Copy类型it is possible that not

相关问题