web-dev-qa-db-fra.com

Pourquoi le compilateur Rust n'optimise-t-il pas le code en supposant que deux références mutables ne peuvent pas être alias?

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;
}

lorsqu'il est compilé par clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final) avec le drapeau -O3, 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 est stocké deux fois dans (%rdi) Au cas où int *a Et int *b Alias.

Lorsque nous disons explicitement au compilateur que ces deux pointeurs ne peuvent pas alias avec le mot clé restrict:

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

Puisque 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 pouvoir émettre la version la plus optimisée du code.

Lorsque je teste avec le code ci-dessous et que je le compile avec 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 peuvent pas être alias.

Est-ce parce que le compilateur Rust Rust est toujours en développement et n'a pas encore incorporé d'analyse d'alias pour faire l'optimisation?

Est-ce parce qu'il y a encore une chance que a et b puissent alias, même dans Rust sécurisé?

290
Zhiyao

Rust à l'origine did active l'attribut noalias de LLVM, mais cela a provoqué une erreur de compilation . Lorsque toutes les versions LLVM prises en charge ne compilent plus le code, il sera réactivé .

Si vous ajoutez -Zmutable-noalias=yes aux options du compilateur, vous obtenez l'assembly attendu:

adds:
        mov     eax, dword ptr [rsi]
        add     eax, eax
        add     dword ptr [rdi], eax
        ret

Autrement dit, Rust mettez l'équivalent du mot-clé restrict de C partout , beaucoup plus répandu que tout autre programme C habituel. Cela a exercé des cas d'angle de LLVM plus qu'il ne pouvait gérer correctement. Il s'avère que les programmeurs C et C++ n'utilisent tout simplement pas restrict aussi souvent que &mut est utilisé dans Rust.

Cela s'est produit plusieurs fois .

  • Rust 1.0 à 1.7 - noalias activé
  • Rust 1.8 à 1.27 - noalias désactivé
  • Rust 1.28 à 1.29 - noalias activé
  • Rouille 1,30 à ??? - noalias désactivé

Problèmes liés Rust

342
Shepmaster