Pour autant que je sache, l'alias de référence / pointeur peut entraver la capacité du compilateur à générer du code optimisé, car ils doivent garantir que le binaire généré se comporte correctement dans le cas où les deux références / pointeurs sont effectivement des alias. Par exemple, dans le code C suivant,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
une fois compilé clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
avec le -O3
drapeau, il émet
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
Ici, le code stocke (%rdi)
deux fois au cas où int *a
et int *b
alias.
Lorsque nous disons explicitement au compilateur que ces deux pointeurs ne peuvent pas être alias avec le restrict
mot clé:
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
Ensuite, Clang émettra une version plus optimisée du code binaire:
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax
2: 01 c0 add %eax,%eax
4: 01 07 add %eax,(%rdi)
6: c3 retq
Étant donné que Rust s'assure (sauf dans le code non sûr) que deux références mutables ne peuvent pas être alias, je pense que le compilateur devrait être capable d'émettre la version la plus optimisée du code.
Lorsque je teste avec le code ci-dessous et que je le compile rustc 1.35.0
avec -C opt-level=3 --emit obj
,
#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
*a += *b;
*a += *b;
}
il génère:
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
Cela ne profite pas de la garantie que a
et b
ne peut pas alias.
Est-ce parce que le compilateur Rust actuel est toujours en développement et n'a pas encore intégré d'analyse d'alias pour faire l'optimisation?
Est-ce parce qu'il y a encore une chance a
et b
pourrait alias, même en toute sécurité Rust?
unsafe
code, les références mutables d'alias ne sont pas autorisées et entraînent un comportement indéfini. Vous pouvez avoir des pointeurs bruts d'alias, mais leunsafe
code ne vous permet pas d'ignorer les règles standard de Rust. C'est juste une idée fausse commune et mérite donc d'être soulignée.+=
opérations dans le corps deadds
peuvent être réinterprétées comme*a = *a + *b + *b
. Si les pointeurs ne font pas alias, ils peuvent, vous pouvez même voir ce qui équivaut àb* + *b
la deuxième liste asm:2: 01 c0 add %eax,%eax
. Mais s'ils font un alias, ils ne peuvent pas, car au moment où vous ajoutez*b
pour la deuxième fois, il contiendra une valeur différente de la première fois (celle que vous stockez en ligne4:
de la première liste asm).Réponses:
Rust à l' origine fait permettre de LLVM
noalias
attribut, mais cela fait un code mal compilée . Lorsque toutes les versions de LLVM prises en charge ne compileront plus le code, il sera réactivé .Si vous ajoutez
-Zmutable-noalias=yes
aux options du compilateur, vous obtenez l'assembly attendu:Autrement dit, Rust a mis l'équivalent du
restrict
mot - clé C partout , bien plus répandu que n'importe quel programme C habituel. Cela a exercé les cas d'angle de LLVM plus qu'il n'était capable de gérer correctement. Il s'avère que les programmeurs C et C ++ n'utilisent tout simplement pasrestrict
aussi fréquemment que&mut
dans Rust.Cela s'est produit plusieurs fois .
noalias
activénoalias
désactivénoalias
activénoalias
désactivéProblèmes de rouille associés
Cas actuel
Cas précédent
Autre
la source
restrict
et se compilent mal sur Clang et GCC. Il n'est pas limité aux langages qui ne sont pas «suffisamment en C ++», sauf si vous comptez C ++ lui-même dans ce groupe .noalias
en compte les pointeurs lors de l'exécution. Il a créé de nouveaux pointeurs basés sur des pointeurs d'entrée, copiant de façon incorrecte l'noalias
attribut même si les nouveaux pointeurs faisaient un alias.