O ile mi wiadomo, aliasing odniesień / wskaźników może utrudniać kompilatorowi generowanie zoptymalizowanego kodu, ponieważ muszą one zapewnić, że wygenerowany plik binarny zachowuje się poprawnie w przypadku, gdy dwa odniesienia / wskaźniki faktycznie są aliasami. Na przykład w poniższym kodzie C
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
po skompilowaniu clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
z -O3
flagą, emituje
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
Tutaj kod przechowuje z powrotem do (%rdi)
dwukrotności w przypadku int *a
i int *b
aliasu.
Gdy wyraźnie informujemy kompilator, że te dwa wskaźniki nie mogą aliasu ze restrict
słowem kluczowym:
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
Następnie Clang wyemituje bardziej zoptymalizowaną wersję kodu binarnego:
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax
2: 01 c0 add %eax,%eax
4: 01 07 add %eax,(%rdi)
6: c3 retq
Ponieważ Rust upewnia się (z wyjątkiem niebezpiecznego kodu), że dwa zmienne odwołania nie mogą być aliasami, pomyślałem, że kompilator powinien być w stanie wyemitować bardziej zoptymalizowaną wersję kodu.
Kiedy testuję poniższy kod i kompiluję go za rustc 1.35.0
pomocą -C opt-level=3 --emit obj
,
#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
*a += *b;
*a += *b;
}
generuje:
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
Nie korzysta to z gwarancji a
i b
nie może być alias.
Czy to dlatego, że obecny kompilator Rust jest wciąż w fazie rozwoju i nie wprowadził jeszcze analizy aliasów w celu optymalizacji?
Czy to dlatego, że jest jeszcze szansa, że a
i b
może alias, nawet w bezpiecznym Rust?
unsafe
kodzie aliasing zmiennych zmiennych nie jest dozwolony i powoduje niezdefiniowane zachowanie. Możesz mieć aliasing surowych wskaźników, ale unsafe
kod w rzeczywistości nie pozwala zignorować standardowych reguł Rust. To tylko powszechne nieporozumienie i dlatego warto na nie zwrócić uwagę.
+=
operacje w ciele adds
można interpretować jako *a = *a + *b + *b
. Jeśli wskaźniki nie alias, mogą one, można nawet zobaczyć, co wynosi b* + *b
w drugim asm ogłoszenia: 2: 01 c0 add %eax,%eax
. Ale jeśli robią alias, nie mogą, ponieważ do czasu dodania *b
po raz drugi, będzie on zawierał inną wartość niż za pierwszym razem (ten, który przechowujesz w wierszu 4:
pierwszego wykazu asm).