# pwphp-test-suite

Benchmark-Suite zum Vergleich von Zend/PHP CLI gegen php2vala- und pwphpbytecode-Varianten.

## Ziele

- Start-up/Parse-Kosten grosser PHP-Dateien sichtbar machen.
- Rekursion, massive Funktionsaufrufe, Mathematik, sequenziellen Code und RAM-Nutzung getrennt messen.
- Zeit, Durchsatz und Peak-RAM erfassen.
- Korrektheit pruefen: `php-cli` ist Referenz; php2vala-/pwphpbytecode-Laeufe muessen denselben Exitcode, dieselbe maschinenlesbare `RESULT`-Zeile (Iterationszahl + Checksum) und exakt denselben stderr liefern. stdout/stderr werden zusaetzlich nach Diagnosemeldungen wie PHP Warning/Fatal/Parse Error gescannt. Voller stdout wird protokolliert, aber nicht bytegenau als Korrektheitskriterium verwendet.
- Beim RAM nicht nur `ru_maxrss` betrachten, sondern aus `/proc/<pid>/smaps_rollup` besonders `Anonymous`, `Private_Clean` und `Private_Dirty` aufzeichnen.

## Schnellstart

```sh
cd ~/Projects/pwphp-test-suite
./bootstrap.sh
./run-performance.sh --repeats=5 --warmups=1
./run-correctness.sh
```

`bootstrap.sh` fuehrt nur Meson-Setup, Case-Generierung und Engine-Builds aus. Die eigentlichen Laeufe passieren danach explizit ueber die Starter.

Performance-Ergebnisse landen unter `results/performance/<timestamp>/`, Correctness-Ergebnisse unter `results/correctness/<timestamp>/`:

- `results.jsonl`: Rohdaten pro Engine/Test/Run
- `summary.md`: lesbare Zusammenfassung
- `benchmark.md`: mehrere Matrizen mit Test-Script als Zeile und Engine als Spalte; je Metrik zuerst eine Build-Weg-Vergleichstabelle (`php-cli`, `php-cli-opcache-nojit`, `php-cli-opcache-jit`, `pwphpbytecode`, `pwphpbytecode-jit`, `php2vala-embed-opt1`, `php2vala-both-opt1`, `php2vala-native-opt1`), danach eine php2vala-Optimizer-Tabelle (`embed`/`both`/`native`, jeweils `opt1..4`). Markdown-Reports formatieren Werte ab 1000 mit Tausendertrennzeichen; CSV/JSON bleiben maschinenlesbar ohne Gruppierung.
- `optimizer-zval-report.md` / `optimizer-zval-report.json`: zaehlt `Php.zval` in den generierten php2vala-`.vala`-Zwischendateien und vergleicht opt2 gegen opt1, opt3 gegen opt2 sowie opt4 gegen opt3. Weniger `Php.zval` ist besser; `0` ist das erreichbare Maximum. Der Markdown-Report wird zusaetzlich an `benchmark.md` angehaengt.
- `summary.csv`: CSV fuer Tabellenkalkulation
- `optimizer-gates.md` / `optimizer-gates.json`: PASS/FAIL-Pruefung fuer opt2/3/4 gegen opt1 und opt4 gegen `php-cli`/`pwphpbytecode`
- `correctness.md`: Abgleich gegen `php-cli` mit Abweichungen
- `correctness-matrix.md`: Matrizen mit Test-Script als Zeile, Engine als Spalte und `OK`, `COMPILE ERROR`, `NO COMMAND` oder gekuerztem Fehlergrund als Zelle; gleiche Build-Weg-/Optimizer-Aufteilung wie `benchmark.md`
- `correctness.json`: maschinenlesbarer Korrektheitsreport
- `build/engine-build/build-report.json`: php2vala/pw-build-meson Build-Erfolge und Compile-Fehler
- `build/engine-build/<engine>/<case>/*-run.sh`: nachvollziehbares ausfuehrbares Startscript fuer die gebaute oder konfigurierte Engine/Case-Kombination
- `build/engine-build/per_case/<case>/<engine>`: Symlink-Ansicht auf dieselben Engine/Case-Ordner fuer schnellen Vergleich pro Testfall

## Meson Targets

```sh
meson compile -C build generate-cases
meson compile -C build build-engines
meson compile -C build prepare
```

Meson-Targets bauen nur. Tests/Benchmarks werden bewusst nicht als Meson-Build-Schritt ausgefuehrt, sondern ueber `run-performance.sh` oder `run-correctness.sh`.

## Engines

Standardmaessig aktiv sind `php-cli`, `php-cli-opcache-nojit`, `php-cli-opcache-jit` sowie php2vala-Varianten ueber echte `pw-build-meson`-Mini-Projekte. Die Zend-CLI-Engines laufen mit `memory_limit=-1`, damit der 100-MB-Startup-Test nicht am PHP-Default-Limit von 128M scheitert, sondern echte Parse-/RAM-Kosten messbar werden:

- `php-cli`: Zend PHP CLI ohne opcache/JIT (`opcache.enable_cli=0`, `opcache.jit=0`, `opcache.jit_buffer_size=0`)
- `php-cli-opcache-nojit`: Zend PHP CLI mit aktivem opcache, aber `opcache.jit_buffer_size=0`
- `php-cli-opcache-jit`: Zend PHP CLI mit aktivem opcache und `opcache.jit=tracing`
- `php2vala-embed-opt1` bis `php2vala-embed-opt4`: `vbin-embed` / `--embed-runtime`, jeweils mit `opt=1..4`
- `php2vala-native-opt1` bis `php2vala-native-opt4`: `vbin-native` / `--native-runtime`, jeweils mit `opt=1..4`
- `php2vala-both-opt1` bis `php2vala-both-opt4`: `vbin` / `--both-runtimes`, jeweils mit `opt=1..4`
- `pwphpbytecode`: `bin` / pwphpbytecode ohne JIT
- `pwphpbytecode-jit`: `bin-jit` / pwphpbytecode mit JIT

Der Build ist fehlertolerant: wenn php2vala einen Testfall noch nicht uebersetzen kann, wird diese Engine/Test-Kombination im `build-report.json` als Compile-Fehler protokolliert; alle erfolgreich gebauten Kombinationen laufen weiter. Die Benchmark- und Correctness-Matrizen zeigen solche Faelle als `COMPILE ERROR`, nicht als uebersprungene Tests. `NO COMMAND` bedeutet dagegen, dass weder ein ausfuehrbarer Command noch ein Build-Fehlerbericht fuer die Kombination existiert und damit die Suite-Konfiguration unvollstaendig ist.

Die mitgelieferte Engine-Liste liegt unter `tools/enginelist.json`. Lokale Overrides gehen optional ueber:

```sh
cp tools/enginelist.json engines.local.json
```

Aus Kompatibilitaetsgruenden akzeptiert der Loader weiterhin `config/engines.example.json`, falls `tools/enginelist.json` in einer aelteren Arbeitskopie fehlt.

Fuer klassische Command-Engines ersetzt die Suite diese Platzhalter:

- `{root}`: Projektwurzel
- `{case}`: PHP-Testdatei
- `{case_id}`: Test-ID
- `{build_dir}`: Engine-Build-Verzeichnis
- `{artifact}`: Zielpfad fuer kompiliertes Artefakt
- `{php_slot}`: PHP-Slot aus Meson-Option `-Dphp_slot=8.3`
- `{abiver}`: ABI-Version aus Meson-Option `-Dabiver=3`

Auch klassische Command-Engines wie `php-cli`, `php-cli-opcache-nojit` und `php-cli-opcache-jit` bekommen pro Testfall ein ausfuehrbares `*-run.sh` unter `build/engine-build/<engine>/<case>/`. Der Runner nutzt diese Wrapper direkt, damit die tatsaechlich ausgefuehrten Befehle im Build-Ordner nachvollziehbar bleiben.

Zusaetzlich legt der Build unter `build/engine-build/per_case/<case>/<engine>` Symlinks auf die bestehende Struktur `build/engine-build/<engine>/<case>` an. Damit bleiben die Artefakte nur einmal vorhanden, sind aber sowohl engine-zentriert als auch case-zentriert browsbar.

Fuer `type: "pw-build-meson"` nutzt die Suite stattdessen diese Felder: `mode` (`embed`, `native`, `both`, `pwphp`, `pwphp-jit`), `optimizer` (`1..4`), `dependencies` und optional `lto`. Daraus werden pro Testfall `meson.build`, `meson_options.txt` und `src/meson.build.pw` erzeugt. LTO ist standardmaessig aus, weil grosse generierte Testfaelle sonst beim Build deutlich schwerer werden als fuer den eigentlichen Benchmark noetig.

Beim Engine-Build entsteht ausserdem `build/engine-build/php-vala-line-report.md` und `.json`. Dieser Zusatzreport vergleicht nur php2vala-Engines mit `opt=1` und `mode` `native`, `embed` oder `both`; CLI- und pwphpbytecode-Engines werden bewusst ignoriert. Verglichen wird die PHP-Quelldatei gegen die erzeugte Vala-Datei, wobei ein fuehrender generierter Vala-Kommentarblock aus der Vala-Zaehlung entfernt wird.

## Testfaelle

Der Generator erzeugt unter `generated/cases/`:

- `startup_100kb.php`, `startup_1mb.php`, `startup_10mb.php`, `startup_100mb.php`: grosse PHP-Dateien mit vielen toten Funktionen, nur ein kleiner Entry wird ausgefuehrt.
- `recursive_calls.php`: rekursive Funktionsaufrufe.
- `sequential_calls.php`: sehr viele sequentielle Funktionsaufrufe.
- `math_int_float.php`: Integer-/Float-Mathematik.
- `straight_code.php`: sehr viel Ausfuehrungscode hintereinander ohne grosse Schleifenlogik im Quelltext.
- `ram_pressure.php`: grosse anonyme Heap-Allokationen.
- `array_hash.php`: assoziative Arrays/Hashmap-Churn.
- `string_concat.php`: String-Verarbeitung/Konkatenation.
- `branching.php`: viele Bedingungen und Spruenge.
- `include_many.php`: viele `require`-Dateien als Zend-Startup-/Filesystem-Vergleich.
- `multi_file_pipeline.php`: mehrere kleine `require`-Dateien mit Cross-File-Funktionspipeline; gezielt fuer opt3/File-Funktions-Optimierung.
- `over_shared_lib.php`: gleicher Funktionsaufruf ueber eine getrennte Bibliotheksgrenze. PHP-CLI nutzt `include`, die php2vala-/pwphpbytecode-Engines bauen eine echte `libover_shared_lib_bench.so` und linken den Benchmark dagegen.
- `api_stub_boundary.php`: typisierte Stub-API-Grenze mit PHP-Fallback und echter Shared-Library fuer php2vala-/pwphpbytecode-Engines; gezielt fuer opt4/API-Grenzen.
- `correct_scalars_ops.php`: Skalare, Arithmetik, Division und Vergleichsoperatoren.
- `correct_control_flow.php`: `if`, `for`, `while`, `break`, `continue`.
- `correct_functions_scope.php`: User-Funktionen, Rekursion und lokale Variablen.
- `correct_arrays.php`: Listen, assoziative Arrays, `isset`, `count`.
- `correct_strings_builtins.php`: `trim`, `substr`, `strlen`, `str_replace`.
- `correct_include_literal.php`: Literal-`require` und Cross-File-Funktionsaufruf.
- `correct_embed_builtins.php`: Builtins, die vor allem fuer Embed/PHP-Fallback relevant sind.
- `correct_runtime_builtins.php`: Runtime-Funktionen aus `a_vala_libs`, u. a. `explode`, `implode`, JSON, `floor`, `round`, `min`, `number_format`, `strpos`, `stripos`, `ltrim`, `rtrim`, `serialize`, `pack`, `ctype_digit`, `is_numeric`, `sprintf`, `time`, `microtime` und `empty`.
- `correct_regex_builtins.php`: `preg_match`, `preg_match_all`, `preg_replace`, `preg_split`.
- `correct_files_process_builtins.php`: `file_exists`, `file_get_contents`, `filemtime`, `escapeshellarg`, `shell_exec`, `exec`.
- `correct_classes_static.php`: Klassen, Konstruktor, Objektfelder und Methoden ohne statische Dispatch-Syntax.
- `correct_modern_control.php`: `foreach`, `switch`, ternary und explizite Fallback-Branches.
- `correct_globals_references.php`: global-aehnlicher Zustand und Update-Helper ohne PHP-Referenzsyntax.
- `tool_php_slots.php`: realistische `pw-build-meson`-Logik zur PHP-Slot-/ABI-Erkennung, inklusive fuehrender `#!/bin/php`-Interpreterzeile.
- `tool_pw_repo_helper.php`: destillierte `pw-repo-helper`-Logik fuer Package-Atom-/PF-Normalisierung, `package.use`-Tokens und USE-Dependency-Klassifizierung.
- `real_pylon_loopback.php`: destillierte `pylon-battery-monitor`-Parser-/Transportlogik mit lokalen Fixture-Antworten statt echter TCP-/Seriell-Verbindung.
- `real_valaphp_libpwcommon.php`: zweistufiger `valaphp-libpwcommon`-artiger Test; php2vala baut erst eine VALAPHP-Library und danach einen Consumer gegen diese Library.
- `real_valaphp_benchmark.php`: `valaphp-benchmark`-artiger Fibonacci-/Validierungsfall.
- `pwphpbytecode_library.php`: zweistufiger pwphpbytecode-Library-Test mit `class`/`lib` und Consumer.
- `pwcomposer_library.php`: pwcomposer-artiger Library-Case fuer pwphpbytecode.
- `php2vala_embed_pwphpbytecode_lib.php`: zweistufiger Test fuer `runtime-embed` mit angebundener pwphpbytecode-Library ueber die eigene Engine `php2vala-pwphp-opt1` (`vbin-pwphp` im generierten `meson.build.pw`).

Weitere sinnvolle Erweiterungen:

- Exception-/Error-Path-Kosten.
- Interface-Dispatch.
- Warm/Cold-Cache-Laeufe getrennt, falls du Kernel-Pagecache explizit kontrollierst.

## Runner-Optionen

```sh
./run-performance.sh --repeats=5 --warmups=1
./run-performance.sh --engines=php-cli,php2vala-native-opt4 --cases=math_int_float,ram_pressure
./run-correctness.sh --cases=correct_scalars_ops,correct_arrays
```

`run-correctness.sh` laesst ohne `--cases` nur die `correct_*`- und `tool_*`-Faelle laufen. Performance-/Stressfaelle wie `startup_*`, `straight_code`, `ram_pressure` und Durchsatz-Benchmarks bleiben fuer `run-performance.sh` bzw. explizite Correctness-Laeufe verfuegbar.

Die Starter nutzen standardmaessig diese Profile:

- `run-performance.sh` misst Performance/RAM und schaltet Correctness-Vergleiche aus.
- `run-performance.sh` prueft zusaetzlich Optimizer-Gates: opt2/3/4 muessen je Case/Mode schneller oder speichersparender als opt1 sein; opt4 wird ausserdem gegen `php-cli` und `pwphpbytecode` verglichen.
- `run-correctness.sh` nutzt `--repeats=1 --warmups=0` und prueft fuer jeden gebauten Nicht-Referenzlauf Exitcode, `RESULT`-Werte und stderr gegen `php-cli`. stdout/stderr werden ausserdem nach Diagnosemeldungen wie PHP Warning/Fatal/Parse Error, Uncaught, Stack trace oder Prozessabbruechen gescannt; solche Meldungen reklamieren auch dann, wenn stdout nicht bytegenau verglichen wird. Voller stdout bleibt im Report zur Diagnose sichtbar. Bei Abweichungen schreibt der Runner `correctness.md`/`correctness.json` und beendet sich mit Exitcode 1.

Fuer explorative Correctness-Laeufe, die trotz Abweichungen weiter als erfolgreich beendet werden sollen:

```sh
./run-correctness.sh --allow-correctness-failures=1
```

`NO_COLOR=1` unterdrueckt Farbausgabe.
