# Exécuter et profiler

> Exécutez un programme invité dans l’émulateur d’Apogee, depuis Rust ou en ligne de commande, comparez-le avec votre compilation pour l’hôte, et découvrez où vont ses cycles avant de payer pour les prouver.

Exécuter un programme invité ne coûte presque rien; le prouver coûte en proportion des cycles qu’il exécute. Exécutez donc d’abord, comparez avec votre compilation pour l’hôte, et examinez le profil des cycles avant de prouver quoi que ce soit.

## Depuis Rust : l’émulateur

`emulator::run` exécute une image chargée sur une entrée publique et des données auxiliaires (*advice*), dans du code hôte, sans preuve :

```rust title="Exécuter un programme invité et le comparer avec la compilation pour l’hôte"
let elf = std::fs::read(elf_path)?;
let image = loader::load_elf(&elf).expect("the ELF loads");
let io = emulator::GuestIo { input: b"hi".to_vec(), advice: Vec::new() };
let run = emulator::run(&image, &io).expect("no fatal error");

assert_eq!(run.exit_code, 0);
assert_eq!(run.io.output, my_app::run(b"hi", &[]).unwrap()); // the host build agrees
println!("{} cycles", run.cycle_count);
```

`run` renvoie une `Execution` : les registres finaux, le statut de sortie, le nombre de cycles et les valeurs publiques. Un statut de sortie non nul est une exécution, pas une erreur, et revient dans `exit_code`. Une erreur fatale de l’exécuteur, comme `OutOfBounds`, `Misaligned` ou `NotAnInstruction`, revient sous la forme d’une `EmuError`, et une telle exécution n’a pas de preuve ([Dépannage](https://apogee.gweb3networks.com/docs/launch/troubleshooting#fatal)).

L’émulateur est une fonction pure de l’image et de l’entrée : ni horloge, ni aléa, ni fils d’exécution. La même entrée donne la même exécution, cycle pour cycle, et c’est aussi ce qui permet au prouveur d’exécuter deux fois et de découper des shards identiques.

## En ligne de commande : le profileur

```sh
cargo run --release -p profiler -- elf <elf> [--input <file>] [--advice <file>] [--top <n>] [--json ]
```

Il exécute le programme invité sur les fichiers donnés, à la plus petite hauteur de table dans laquelle tient son code, et affiche un rapport. Ses chiffres sont des décomptes de cycles exécutés, identiques sur toute machine.

```text
workload
  label                        hello
  guest cycles                 114
  exit status                  0
  journal bytes                13

cycles by semantic workload
  core runtime                             94   82.46%
  unattributed                             20   17.54%

cycles by family
  ADD_SUB_LUI_AUIPC            64
  JUMP_BRANCH_SLT              21
  MEM_WORD                     3
  MEM_SUBWORD                  26

top functions
            94   82.46%          1 calls        94.0 c/call  guest_sdk::commit  [core runtime]
             8    7.02%          1 calls         8.0 c/call  main  [unattributed]
```

Comment le lire :

- La section **cycles by family** montre ce que vous payez. Chaque famille qui a des lignes coûte au moins un shard de sa hauteur, et plus de cycles dans une famille signifie plus de shards de cette famille.
- La section **top functions** impute à chaque fonction ses propres cycles, y compris tout ce que le compilateur y a intégré en ligne, mais pas ceux des fonctions qu’elle appelle. Les appels sont comptés à la première instruction de la fonction.
- La section **cycles by semantic workload** regroupe les fonctions en quatorze catégories selon leur nom, comme le hachage, les signatures et l’environnement d’exécution de base. La part non attribuée et la répartition des mnémoniques servent de contrôle à cette attribution, puisqu’aucune table des symboles ne peut les étiqueter de travers.
- La section **accelerator candidates** chiffre les délégations qu’une version future pourrait ajouter, sous forme de plafond : voir [Délégations](https://apogee.gweb3networks.com/docs/launch/delegations#pricing).

Le profileur a deux autres verbes, pour la charge de travail Ethereum : `block <stem>` exécute le programme invité revm sur des données de référence enregistrées, et `record <number|latest>` enregistre un bloc à partir de `ETH_RPC_URL` et l’exécute.

## Réduire le coût

L’ordre qui est généralement rentable :

1. **Compilez avec `--release`.** L’optimisation retire d’un quart à plus de la moitié des instructions d’un programme invité.
2. **Déléguez le hachage et l’arithmétique de courbe.** Utilisez `guest_sdk::keccak256`, `sha256`, `ec_add` et les versions embarquées de `k256` et `ark-ff`, au lieu de compiler une implémentation logicielle dans le programme invité.
3. **Cessez d’allouer dans les boucles.** Chaque allocation coûte des instructions, et avec un allocateur linéaire, c’est aussi de la mémoire que vous ne récupérez jamais ([le tas](https://apogee.gweb3networks.com/docs/launch/write#heap)).
4. **Vérifiez au lieu de calculer.** Si un résultat est coûteux à trouver et peu coûteux à vérifier, comme un ordre trié, une racine carrée ou un chemin dans un arbre, laissez le prouveur le fournir sous forme de données auxiliaires et faites-le vérifier par le programme invité.
5. **Évitez la virgule flottante.** Elle se compile en routines logicielles; l’arithmétique entière et en virgule fixe coûte bien moins cher.

Puis mesurez de nouveau. Les décomptes de cycles sont exacts et reproductibles : chaque changement se traduit par un nombre.
