# Ausführen und profilieren

> Führen Sie ein Gastprogramm im Emulator von Apogee aus, aus Rust oder von der Kommandozeile, vergleichen Sie es mit Ihrem Host-Build und finden Sie heraus, wohin seine Zyklen gehen, bevor Sie dafür bezahlen, sie zu beweisen.

Ein Gastprogramm (Guest) auszuführen kostet fast nichts; es zu beweisen kostet proportional zu den Zyklen, die es ausführt. Führen Sie es also zuerst aus, vergleichen Sie es mit Ihrem Host-Build und sehen Sie sich das Zyklenprofil an, bevor Sie irgendetwas beweisen.

## Aus Rust: der Emulator

`emulator::run` führt ein geladenes Image über einer öffentlichen Eingabe und Hilfsdaten (Advice) aus, in Host-Code, ohne Beweis:

```rust title="Ein Gastprogramm ausführen und mit dem Host-Build vergleichen"
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` gibt eine `Execution` zurück: die finalen Register, den Exit-Status, die Zyklenzahl und die öffentlichen Werte. Ein Exit-Status ungleich null ist eine Ausführung, kein Fehler, und kommt als `exit_code` zurück. Ein fataler Fehler des Executors, etwa `OutOfBounds`, `Misaligned` oder `NotAnInstruction`, kommt als `EmuError` zurück, und ein solcher Lauf hat keinen Beweis ([Fehlerbehebung](https://apogee.gweb3networks.com/docs/launch/troubleshooting#fatal)).

Der Emulator ist eine reine Funktion des Images und der Eingabe: keine Uhr, kein Zufall, keine Threads. Dieselbe Eingabe ergibt dieselbe Ausführung, Zyklus für Zyklus, und genau das erlaubt es auch dem Prover, zweimal auszuführen und identische Shards zu schneiden.

## Von der Kommandozeile: der Profiler

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

Er führt das Gastprogramm über den angegebenen Dateien bei der kleinsten Tabellenhöhe aus, in die sein Code passt, und gibt einen Bericht aus. Seine Zahlen sind Zählungen ausgeführter Zyklen, auf jeder Maschine dieselben.

```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]
```

So lesen Sie ihn:

- Der Abschnitt **cycles by family** zeigt, wofür Sie bezahlen. Jede Familie mit Zeilen kostet mindestens einen Shard ihrer Höhe, und mehr Zyklen in einer Familie bedeuten mehr Shards davon.
- Der Abschnitt **top functions** rechnet jeder Funktion ihre eigenen Zyklen an, einschließlich allem, was der Compiler per Inlining in sie übernommen hat, aber nicht die der von ihr aufgerufenen Funktionen. Aufrufe werden am ersten Befehl der Funktion gezählt.
- Der Abschnitt **cycles by semantic workload** ordnet Funktionen anhand ihres Namens vierzehn Kategorien zu, etwa Hashing, Signaturen und dem Kern der Laufzeitumgebung. Der nicht zugeordnete Anteil und die Verteilung der Mnemonics dienen als Kontrolle dieser Zuordnung, denn keine Symboltabelle kann sie falsch beschriften.
- Der Abschnitt **accelerator candidates** bepreist die Delegationen, die eine künftige Version hinzufügen könnte, als Obergrenze: siehe [Delegationen](https://apogee.gweb3networks.com/docs/launch/delegations#pricing).

Der Profiler hat zwei weitere Unterbefehle für die Ethereum-Arbeitslast: `block <stem>` führt das revm-Gastprogramm über einer aufgezeichneten Fixture aus, und `record <number|latest>` zeichnet einen Block von `ETH_RPC_URL` auf und führt ihn aus.

## Günstiger machen

Die Reihenfolge, die sich meist auszahlt:

1. **Bauen Sie mit `--release`.** Die Optimierung entfernt ein Viertel bis über die Hälfte der Befehle eines Gastprogramms.
2. **Delegieren Sie Hashing und Kurvenarithmetik.** Verwenden Sie `guest_sdk::keccak256`, `sha256`, `ec_add` und die mitgelieferten `k256` und `ark-ff`, statt eine Software-Implementierung in das Gastprogramm zu kompilieren.
3. **Hören Sie auf, in Schleifen zu allozieren.** Jede Allokation kostet Befehle, und mit einem Bump-Allokator ist sie außerdem Speicher, den Sie nie zurückbekommen ([der Heap](https://apogee.gweb3networks.com/docs/launch/write#heap)).
4. **Prüfen statt berechnen.** Ist ein Ergebnis teuer zu finden und günstig zu verifizieren, etwa eine Sortierreihenfolge, eine Quadratwurzel oder ein Pfad durch einen Baum, lassen Sie es den Prover als Hilfsdaten liefern und das Gastprogramm es verifizieren.
5. **Vermeiden Sie Gleitkommaarithmetik.** Sie wird zu Softwareroutinen kompiliert; Ganzzahl- und Festkommaarithmetik sind weit günstiger.

Messen Sie dann erneut. Zyklenzahlen sind exakt und reproduzierbar; jede Änderung zeigt sich also als Zahl.
