web-dev-qa-db-fra.com

Pourquoi les Rust exécutables sont-ils si énormes?

Après avoir trouvé Rust et lu les deux premiers chapitres de la documentation, je trouve l’approche et la façon dont ils ont défini le langage sont particulièrement intéressants. J’ai donc décidé de me mouiller les doigts et de commencer par Bonjour le monde...

Je l'ai fait sur Windows 7 x64, d'ailleurs.

fn main() {
    println!("Hello, world!");
}

Emission cargo build et en regardant le résultat dans targets\debug J'ai trouvé le résultat .exe étant 3MB. Après quelques recherches (la documentation des drapeaux de ligne de commande cargo est difficile à trouver ...), j'ai trouvé --release _ option et créé la version release. À ma grande surprise, la taille du fichier .exe n'a été réduite que de 2,99 Mo au lieu de 3 Mo.

Donc, en avouant que je suis un débutant pour Rust et son écosystème, je m'attendais à ce qu'un langage de programmation système produise quelque chose de compact.

Quelqu'un peut-il préciser ce que Rust compile dans, comment il est possible qu'il produise des images aussi volumineuses à partir d'un programme à trois lignes? Compile-t-il sur une machine virtuelle? Existe-t-il une commande en bande que j'ai manquée? (informations de débogage dans la version de compilation?)? Y at-il autre chose qui pourrait permettre de comprendre ce qui se passe?

118
BitTickler

Rust utilise des liaisons statiques pour compiler ses programmes, ce qui signifie que toutes les bibliothèques requises même par les plus simples Hello world! programme sera compilé dans votre exécutable. Ceci inclut également le moteur d'exécution Rust).

Pour forcer Rust pour lier dynamiquement des programmes, utilisez les arguments de ligne de commande -C prefer-dynamic; cela entraînera une taille de fichier beaucoup plus petite mais nécessitera également la Rust bibliothèques (y compris son runtime) pour être disponible pour votre programme au moment de l'exécution. Cela signifie essentiellement que vous devrez les fournir si l'ordinateur ne les a pas, en prenant plus l’espace que votre programme original lié de manière statique prend.

Pour la portabilité, je vous recommanderais de lier statiquement les bibliothèques Rust et l’exécution) de la même manière que si vous distribuiez jamais vos programmes à d’autres.

112
AStopher

Je n'ai pas de système Windows à essayer, mais sous Linux, un monde compilé statiquement Rust hello est en réalité plus petit que son équivalent C. Si vous constatez une énorme différence de taille, Cela est probablement dû au fait que vous liez l'exécutable Rust de manière statique et l'exécutable C de manière dynamique).

Avec la liaison dynamique, vous devez également tenir compte de la taille de toutes les bibliothèques dynamiques, et pas seulement de l’exécutable.

Donc, si vous voulez comparer des pommes avec des pommes, vous devez vous assurer que les deux sont dynamiques ou que les deux sont statiques. Différents compilateurs auront des valeurs par défaut différentes, vous ne pouvez donc pas vous fier aux valeurs par défaut du compilateur pour obtenir le même résultat.

Si cela vous intéresse, voici mes résultats:

 - rw-r - r-- 1 aij aij 63 avril 5 14:26 printf.c 
 - rwxr-xr-x 1 aij aij 6696 5 avril 14:27 printf.dyn 
 - rwxr-xr-x 1 aij aij 829344 5 avril 14:27 printf.static 
 - rw-r - r-- 1 aij aij 59 5 avril 14:26 put.c 
 - rwxr-xr-x 1 aij aij 6696 5 avril 14:27 puts.dyn 
 - rwxr-xr-x 1 aij aij 829344 5 avril 14:27 puts.static 
 - rwxr-xr-x 1 aij aij 8712 5 avril 14:28 Rust.dyn 
 - rw-r - r-- 1 aij aij 46 avril 5 14:09 Rust.rs 
 - rwxr -xr-x 1 aij aij 661496 5 avril 14:28 Rust.static 

Celles-ci ont été compilées avec gcc (Debian 4.9.2-10) 4.9.2 et rustc 1.0.0-nightly (d17d6e7f1 2015-04-02) (version 2015-04-03), toutes deux avec des options par défaut et avec -static pour gcc et -C prefer-dynamic pour rustc.

J'avais deux versions du monde C hello parce que je pensais qu'utiliser puts() pourrait être lié à moins d'unités de compilation.

Si vous voulez essayer de le reproduire sous Windows, voici les sources que j'ai utilisées:

printf.c:

#include <stdio.h>
int main() {
  printf("Hello, world!\n");
}

met.c:

#include <stdio.h>
int main() {
  puts("Hello, world!");
}

Rust.rs

fn main() {
    println!("Hello, world!");
}

N'oubliez pas non plus que des quantités différentes d'informations de débogage ou des niveaux d'optimisation différents peuvent également faire la différence. Mais je pense que si vous constatez une différence énorme, cela est dû au lien statique/dynamique.

53
aij

Lors de la compilation avec Cargo, vous pouvez utiliser un lien dynamique:

cargo rustc --release -- -C prefer-dynamic

Cela réduira considérablement la taille du binaire, car il est maintenant lié dynamiquement.

Sous Linux, vous pouvez au moins supprimer le binaire des symboles à l’aide de la commande strip:

strip target/release/<binary>

Cela permettra de réduire de moitié environ la taille de la plupart des fichiers binaires.

23

Pour un aperçu de toutes les manières de réduire la taille d'un binaire Rust, voir le min-sized-Rust référentiel.

Les étapes de haut niveau actuelles pour réduire la taille binaire sont les suivantes:

  1. Utilisez Rust 1.32.0 ou plus récent (ce qui n'inclut pas jemalloc. Par défaut))
  2. Ajouter ce qui suit à Cargo.toml
[profile.release]
opt-level = 'z'     # Optimize for size.
lto = true          # Enable Link Time Optimization
codegen-units = 1   # Reduce number of codegen units to increase optimizations.
panic = 'abort'     # Abort on panic
  1. Construire en mode release en utilisant cargo build --release
  2. Exécutez strip sur le binaire résultant.

Il est possible de faire plus avec nightly Rust, mais je vais laisser cette information dans min-sized-Rust car il évolue dans le temps en raison de l’utilisation de fonctions instables.

Vous pouvez aussi utiliser #![no_std] pour supprimer le libstd de Rust. Voir min-sized-Rust pour plus de détails.

11
phoenix