Sejauh yang saya tahu, referensi / pointer aliasing dapat menghambat kemampuan kompiler untuk menghasilkan kode yang dioptimalkan, karena mereka harus memastikan biner yang dihasilkan berperilaku dengan benar dalam kasus di mana dua referensi / pointer memang alias. Misalnya, dalam kode C berikut,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
ketika dikompilasi oleh clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
dengan -O3
bendera, itu memancarkan
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
Di sini kode menyimpan kembali (%rdi)
dua kali dalam kasus int *a
dan int *b
alias.
Ketika kami secara eksplisit memberi tahu kompiler bahwa kedua petunjuk ini tidak dapat alias dengan restrict
kata kunci:
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
Kemudian Dentang akan mengeluarkan versi yang lebih optimal dari kode biner:
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax
2: 01 c0 add %eax,%eax
4: 01 07 add %eax,(%rdi)
6: c3 retq
Karena Rust memastikan (kecuali dalam kode yang tidak aman) bahwa dua referensi yang bisa diubah tidak bisa alias, saya akan berpikir bahwa kompiler harus dapat memancarkan versi kode yang lebih optimal.
Ketika saya menguji dengan kode di bawah ini dan mengompilasinya rustc 1.35.0
dengan -C opt-level=3 --emit obj
,
#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
*a += *b;
*a += *b;
}
itu menghasilkan:
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
Ini tidak memanfaatkan jaminan itu a
dan b
tidak bisa alias.
Apakah ini karena compiler Rust saat ini masih dalam pengembangan dan belum memasukkan analisis alias untuk melakukan optimasi?
Apakah ini karena masih ada kemungkinan itu a
dan b
bisa alias, bahkan di Rust yang aman?
unsafe
kode, alias referensi yang dapat diubah tidak diperbolehkan dan menghasilkan perilaku yang tidak ditentukan. Anda dapat memiliki alias mentah pointer, tetapiunsafe
kode sebenarnya tidak memungkinkan Anda untuk mengabaikan aturan standar Rust. Itu hanya kesalahpahaman umum dan dengan demikian layak untuk ditunjukkan.+=
operasi dalam tubuhadds
dapat ditafsirkan kembali sebagai*a = *a + *b + *b
. Jika pointer tidak alias, mereka bisa, Anda bahkan dapat melihat apa yang berjumlahb* + *b
di asm kedua listing:2: 01 c0 add %eax,%eax
. Tetapi jika mereka melakukan alias, mereka tidak bisa, karena pada saat Anda menambahkan*b
untuk kedua kalinya, itu akan berisi nilai yang berbeda dari yang pertama kali ada (yang Anda simpan pada baris4:
daftar asm pertama).Jawaban:
Rust awalnya memang mengaktifkan
noalias
atribut LLVM , tetapi ini menyebabkan kode yang salah dikompilasi . Ketika semua versi LLVM yang didukung tidak lagi mengkompilasi kode, itu akan diaktifkan kembali .Jika Anda menambahkan
-Zmutable-noalias=yes
ke opsi kompiler, Anda mendapatkan unit yang diharapkan:Sederhananya, Rust menempatkan setara dengan
restrict
kata kunci C di mana-mana , jauh lebih lazim daripada program C biasa. Ini menggunakan kasus sudut LLVM lebih daripada yang bisa ditangani dengan benar. Ternyata programmer C dan C ++ tidak menggunakanrestrict
sesering&mut
yang digunakan di Rust.Ini telah terjadi beberapa kali .
noalias
diaktifkannoalias
dinonaktifkannoalias
diaktifkannoalias
dinonaktifkanMasalah karat terkait
Kasus saat ini
Kasus sebelumnya
Lain
sumber
restrict
dan salah mengompilasi pada Dentang dan GCC. Ini tidak terbatas pada bahasa yang tidak “cukup C ++”, kecuali jika Anda menghitung C ++ itu sendiri di grup itu .noalias
pointer ketika mengeksekusi. Itu menciptakan pointer baru berdasarkan input pointer, menyalinnoalias
atribut secara tidak benar meskipun pointer baru melakukan alias.