Theo như tôi biết, bí danh tham chiếu / con trỏ có thể cản trở khả năng tạo mã được tối ưu hóa của trình biên dịch, vì chúng phải đảm bảo nhị phân được tạo hoạt động chính xác trong trường hợp hai tham chiếu / con trỏ thực sự là bí danh. Ví dụ, trong mã C sau đây,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
khi biên soạn bởi clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
với -O3
cờ, nó phát ra
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
Ở đây mã lưu trữ trở lại (%rdi)
hai lần trong trường hợp int *a
và int *b
bí danh.
Khi chúng tôi nói rõ với trình biên dịch rằng hai con trỏ này không thể bí danh với restrict
từ khóa:
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
Sau đó, Clang sẽ phát ra một phiên bản tối ưu hơn của mã nhị phân:
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax
2: 01 c0 add %eax,%eax
4: 01 07 add %eax,(%rdi)
6: c3 retq
Vì Rust đảm bảo (ngoại trừ mã không an toàn) rằng hai tham chiếu có thể thay đổi không thể bí danh, tôi sẽ nghĩ rằng trình biên dịch sẽ có thể phát ra phiên bản mã được tối ưu hóa hơn.
Khi tôi kiểm tra mã bên dưới và biên dịch nó rustc 1.35.0
với -C opt-level=3 --emit obj
,
#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
*a += *b;
*a += *b;
}
nó tạo ra:
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
Điều này không tận dụng sự đảm bảo rằng a
và b
không thể bí danh.
Đây có phải là do trình biên dịch Rust hiện tại vẫn đang được phát triển và chưa kết hợp phân tích bí danh để thực hiện tối ưu hóa?
Đây có phải là vì vẫn còn cơ hội a
và b
có thể bí danh, ngay cả trong Rust an toàn?
unsafe
mã, các tham chiếu có thể thay đổi không được phép và dẫn đến hành vi không xác định. Bạn có thể có các con trỏ thô có răng cưa, nhưng unsafe
mã không thực sự cho phép bạn bỏ qua các quy tắc chuẩn của Rust. Đó chỉ là một quan niệm sai lầm phổ biến và do đó đáng để chỉ ra.
+=
thao tác trong cơ adds
thể có thể được diễn giải lại như thế nào *a = *a + *b + *b
. Nếu con trỏ không bí danh, chúng có thể, bạn thậm chí có thể thấy số tiền b* + *b
trong danh sách mã asm thứ hai : 2: 01 c0 add %eax,%eax
. Nhưng nếu họ làm bí danh, họ không thể, bởi vì khi bạn thêm *b
lần thứ hai, nó sẽ chứa một giá trị khác với lần đầu tiên (lần bạn lưu trữ trên 4:
danh sách asm đầu tiên).