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, aleunsafe
kod w rzeczywistości nie pozwala zignorować standardowych reguł Rust. To tylko powszechne nieporozumienie i dlatego warto na nie zwrócić uwagę.+=
operacje w cieleadds
można interpretować jako*a = *a + *b + *b
. Jeśli wskaźniki nie alias, mogą one, można nawet zobaczyć, co wynosib* + *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 wierszu4:
pierwszego wykazu asm).Odpowiedzi:
Rdza pierwotnie nie pozwalają LLVM za
noalias
atrybut, ale ten kod spowodowanych miscompiled . Gdy wszystkie obsługiwane wersje LLVM nie będą już błędnie kompilować kodu, zostanie on ponownie włączony .Jeśli dodasz
-Zmutable-noalias=yes
do opcji kompilatora, otrzymasz oczekiwany zestaw:Mówiąc najprościej, Rust umieścił wszędzie odpowiednik
restrict
słowa kluczowego C , o wiele bardziej rozpowszechniony niż jakikolwiek zwykły program C. To ćwiczyło narożne przypadki LLVM więcej niż było w stanie poprawnie obsłużyć. Okazuje się, że programiści C i C ++ po prostu nie używają tak często, jak w Rust.restrict
&mut
Stało się to wiele razy .
noalias
włączonanoalias
wyłączonenoalias
włączonynoalias
wyłączonePowiązane problemy z rdzą
Obecna sprawa
Poprzednia sprawa
Inny
źródło
restrict
i błędnie kompilują zarówno w Clang, jak i GCC. Nie ogranicza się to do języków, które nie wystarczają na „C ++”, chyba że sam policzysz C ++ w tej grupie .noalias
wskaźników podczas wykonywania. Utworzono nowe wskaźniki na podstawie wskaźników wejściowych, niepoprawnie kopiującnoalias
atrybut, mimo że nowe wskaźniki miały alias.