据我所知,引用/指针别名可能会妨碍编译器生成优化代码的能力,因为它们必须确保生成的二进制文件在两个引用/指针确实别名的情况下正确运行.例如,在下面的C代码中,

void adds(int  *a, int *b) {
    *a += *b;
    *a += *b;
}

当使用-O3标志按clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)编译时,它会发出

0000000000000000 <adds>:
   0:    8b 07                    mov    (%rdi),%eax
   2:    03 06                    add    (%rsi),%eax
   4:    89 07                    mov    %eax,(%rdi)  # The first time
   6:    03 06                    add    (%rsi),%eax
   8:    89 07                    mov    %eax,(%rdi)  # The second time
   a:    c3                       retq

在这里,代码在情况int *aint *b中两次存储回(%rdi).

当我们明确告诉编译器这两个指针不能与restrict关键字别名时:

void adds(int * restrict a, int * restrict b) {
    *a += *b;
    *a += *b;
}

然后,Clang将发出更优化的二进制代码:

0000000000000000 <adds>:
   0:    8b 06                    mov    (%rsi),%eax
   2:    01 c0                    add    %eax,%eax
   4:    01 07                    add    %eax,(%rdi)
   6:    c3                       retq

由于Rust确保(在不安全的代码中除外)两个可变引用不能别名,我认为编译器应该能够发出更优化的代码版本.

当我用下面的代码测试并用rustc 1.35.0-C opt-level=3 --emit obj编译它时,

#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
    *a += *b;
    *a += *b;
}

它产生:

0000000000000000 <adds>:
   0:    8b 07                    mov    (%rdi),%eax
   2:    03 06                    add    (%rsi),%eax
   4:    89 07                    mov    %eax,(%rdi)
   6:    03 06                    add    (%rsi),%eax
   8:    89 07                    mov    %eax,(%rdi)
   a:    c3                       retq

这并没有利用ab不能使用别名的保证.

这是因为当前的Rust编译器仍在开发中,还没有结合别名分析来进行优化吗?

这是不是因为ab仍然有可能被替代,即使是在安全的情况下?

推荐答案

Rust最初启用LLVM的noalias属性,但这是caused miscompiled code.当LLit will be re-enabled不再支持所有版本的代码时.

如果向编译器选项中添加-Zmutable-noalias=yes,则会得到预期的程序集:

adds:
        mov     eax, dword ptr [rsi]
        add     eax, eax
        add     dword ptr [rdi], eax
        ret

简单地说,Rust put相当于C的restrict关键字everywhere,比任何常见的C程序都流行.这使LLVM的情况超出了它能够正确处理的范围.事实证明,C和C++程序员不使用restrict,因为在 rust 中使用的频率高达&mut.

这已经发生了multiple times次.

  • rust 蚀1.0至1.7-启用noalias
  • rust 蚀1.8至1.27-noalias禁用
  • rust 蚀1.28至1.29-启用noalias
  • rust 蚀1.30至1.54-noalias禁用
  • rust 1.54透???-noalias根据编译器使用的LLVM版本有条件地启用

相关Rust 问题

Rust相关问答推荐

属性宏修改派生宏的派生实现

如何从使用mockall模拟的方法中返回self?

从特征实现调用函数的Rust惯用方法

如何将`Join_all``Vec<;Result<;Vec<;Foo&>;,Anywhere::Error&>;`合并到`Result<;Vec<;Foo&>;,Anywhere::Error&>;`

有没有更好的方法从HashMap的条目初始化 struct ?

如何提高自定义迭代器的`extend`性能

原始数组数据类型的默认trait实现

如何为 struct 字段设置新值并在Ruust中的可变方法中返回旧值

不同类型泛型的映射

是否可以使用Serde/Rust全局处理无效的JSON值?

在为第三方 struct 实现第三方特征时避免包装器的任何方法

通过异常从同步代码中产生yield 是如何工作的?

当我编译 Rust 代码时,我是否缺少 AVX512 的目标功能?

为什么需要静态生命周期以及在处理 Rust 迭代器时如何缩小它?

将 &str 或 String 保存在变量中

如何从trait方法返回std :: iter :: Map?

使用 Rust 从 Raspberry Pi Pico 上的 SPI 读取值

bcrypt 有长度限制吗?

当 `T` 没有实现 `Debug` 时替代 `unwrap()`

有没有办法在 Rust 中对 BigInt 进行正确的位移?