据我所知,引用/指针别名会阻碍编译器生成优化代码的能力,因为它们必须确保在两个引用/指针确实是别名的情况下,生成的二进制文件的行为正确。例如,在以下C代码中,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
当clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
用-O3
标志编译时,它发出
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
下面的代码存回(%rdi)
两次的情况下,int *a
和int *b
别名。
当我们明确告诉编译器这两个指针不能使用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
这不采取保证的优势a
,并b
不能别名。
这是因为当前的Rust编译器仍在开发中,并且尚未合并别名分析来进行优化吗?
这是因为即使在安全的Rust中,仍然有可能a
并b
可能出现别名吗?
3
godbolt.org/z/aEDINX,很奇怪
—
Stargateur,
旁注:“ 由于Rust确保(不安全代码中除外)两个可变引用不能别名 ” –值得一提的是,即使在
—
卢卡斯·卡尔伯托德
unsafe
代码中,也不允许使用别名可变引用并导致未定义行为。您可以使用别名原始指针,但是unsafe
代码实际上不允许您忽略Rust标准规则。这只是一个普遍的误解,因此值得指出。
我花了一些时间弄清楚示例的内容,因为我不懂asm,所以万一它对其他人有帮助:归结为是否可以将
—
dlukes
+=
正文中的两个操作adds
重新解释为*a = *a + *b + *b
。如果指针没有别名,它们可以别名,您甚至可以b* + *b
在第二个asm清单中查看内容2: 01 c0 add %eax,%eax
。但是,如果它们使用别名,则它们不能这样做,因为*b
到第二次添加时,它将包含与第一次(4:
在第一个asm列表的行中存储的值)不同的值。