web-dev-qa-db-fra.com

Quelle est la surcharge du type d'option de Rust?

Dans Rust, les références ne peuvent jamais être nulles, donc dans le cas où vous avez réellement besoin de null, comme une liste chaînée, vous utilisez le type Option:

struct Element {
    value: i32,
    next: Option<Box<Element>>,
}

Combien de temps système est impliqué dans cela en termes d'allocation de mémoire et d'étapes de déréférencement par rapport à un simple pointeur? Existe-t-il un peu de "magie" dans le compilateur/runtime pour rendre Option gratuit ou moins coûteux que si l'on devait implémenter Option par soi-même dans une bibliothèque non-core utilisant le même enum construct, ou en enveloppant le pointeur dans un vecteur?

76
Thilo

Oui, il existe de la magie de compilation qui optimise Option<ptr> vers un seul pointeur (la plupart du temps).

use std::mem::size_of;

macro_rules! show_size {
    (header) => (
        println!("{:<22} {:>4}    {}", "Type", "T", "Option<T>");
    );
    ($t:ty) => (
        println!("{:<22} {:4} {:4}", stringify!($t), size_of::<$t>(), size_of::<Option<$t>>())
    )
}

fn main() {
    show_size!(header);
    show_size!(i32);
    show_size!(&i32);
    show_size!(Box<i32>);
    show_size!(&[i32]);
    show_size!(Vec<i32>);
    show_size!(Result<(), Box<i32>>);
}

Les tailles suivantes sont imprimées (sur une machine 64 bits, les pointeurs font donc 8 octets):

// As of Rust 1.22.1
Type                      T    Option<T>
i32                       4    8
&i32                      8    8
Box<i32>                  8    8
&[i32]                   16   16
Vec<i32>                 24   24
Result<(), Box<i32>>      8   16

Notez que &i32, Box, &[i32], Vec<i32> tous utilisent l'optimisation du pointeur non nullable dans un Option!

71
huon

Cette réponse est désormais obsolète; le discriminant dans Option<T> est maintenant optimisé dans la mesure du possible. (Le reste des informations fournies est cependant toujours intéressant.)

Pour l'instant, un type Option occupe la même quantité d'espace que tout autre type enum. Je ne connais pas les détails, mais il est certainement représenté comme une sorte d'union discriminée.

La possibilité de modifier la représentation interne pour l'optimisation est envisagée par les développeurs Rust.

Voici une discussion pertinente sur la liste de diffusion des développeurs , publiée par Patrick Walton:

J'hésite un peu à m'engager sur une représentation particulière des énumérations, car il y a beaucoup de place pour l'optimisation du compilateur ici. Par exemple, nous pouvons vouloir réduire Option<~int> En un pointeur nullable, nous pouvons vouloir réduire Result<(),~str> en une chaîne nullable, ou nous pouvons vouloir réduire Either<u8,~str> En 1 Word, en supposant que les chaînes ne peuvent jamais occuper les 256 premiers octets de l'espace d'adressage. J'ai pensé pendant un certain temps qu'il vaut peut-être mieux dire que le modèle de bits de Rust énumérations n'est pas spécifié, pour nous donner autant de place que possible pour jouer avec les optimisations.

7
barjak