
# Parallele Worker

Greph verteilt Text- und AST-Scans auf einen Worker-Pool, wenn der Arbeitsumfang den Mehraufwand rechtfertigt. Der Pool basiert auf `pcntl_fork()` und wechselt zur Ausführung im selben Prozess, wenn `ext-pcntl` nicht verfügbar ist (Windows, einige PaaS-Umgebungen, eingeschränktes Hosting).

## Parallele Scans aktivieren

Setze `-j N` im CLI oder `jobs: N` im entsprechenden Optionsobjekt:

```bash
./vendor/bin/greph -F -j 8 "function" src
./vendor/bin/greph -p '$obj->$method($$$ARGS)' -j 4 src
./vendor/bin/rg --threads 8 -F "function" src
./vendor/bin/sg run --threads 8 --pattern '$obj->$method($$$ARGS)' src
```

```php
use Greph\Greph;
use Greph\Text\TextSearchOptions;

Greph::searchText(
    'function',
    'src',
    new TextSearchOptions(fixedString: true, jobs: 8),
);
```

## Wann Parallelisierung einsetzt

Greph parallelisiert nicht bedingungslos. N Prozesse zu starten und ihre Ergebnisse über Pipes hin und her zu übertragen, verursacht Mehraufwand. Bei kleinen Dateilisten überwiegt dieser Aufwand die eigentliche Suche. Deshalb berechnet die Fassade anhand der Auftragszahl, des Suchmodus und der Musterform einen Schwellenwert. Nur Dateilisten oberhalb dieses Schwellenwerts werden an den Worker-Pool verteilt.

Die aktuellen Heuristiken (in `Greph\Greph::shouldUseTextWorkers` und verwandten Komponenten):

- Läufe mit einem einzelnen Job (`-j 1`) werden immer im selben Prozess ausgeführt.
- Bei standardmäßigen Textscans sind ungefähr `jobs * 2_000` Dateien nötig, bevor der Pool aktiviert wird.
- Modi für Trefferanzahl, Dateien mit Treffern und Dateien ohne Treffer senken den Schwellenwert auf ungefähr `jobs * 750`, da die Arbeit pro Datei günstiger ist.
- Bei kurzen Feststringmustern in Form von Bezeichnern steigt der Schwellenwert auf ungefähr `jobs * 4_000`, da der schnelle Pfad für Literale bereits so günstig ist, dass sich das Aufteilen auf Prozesse selten lohnt.
- AST-Suche und AST-Umschreibung verwenden den Schwellenwert `jobs * 750`.

Diese Zahlen sind Richtwerte, keine Garantien. Sie sind auf das WordPress-Benchmark-Korpus abgestimmt und können überarbeitet werden.

## Funktionsweise des Pools

`Greph\Parallel\WorkSplitter` verteilt die Dateiliste auf N Abschnitte. `Greph\Parallel\WorkerPool` startet einen Worker-Prozess pro Abschnitt. Jeder Worker führt dieselbe Closure für seinen Teil aus, und `Greph\Parallel\ResultCollector` liest die serialisierten Ergebnisse über ein Unix-Socket-Paar pro Worker zurück. Der übergeordnete Prozess wartet auf alle Worker und führt dann die Ergebnisse zusammen und sortiert sie erneut, damit die Ausgabereihenfolge unabhängig von der Aufteilung stabil bleibt.

Im Textmodus verwendet der Worker-Pool einen benutzerdefinierten Übertragungs-Codec (`Greph\Text\TextResultCodec`), um die serialisierte Nutzlast kompakt zu halten. Der AST-Modus verwendet PHPs standardmäßiges `serialize()`.

## Wenn pcntl nicht verfügbar ist

Wenn `pcntl_fork` nicht aufrufbar ist (Windows, gehärtete PHP-Builds), wechselt der Pool nahtlos zur Ausführung jedes Abschnitts im selben Prozess. Das Ergebnis entspricht `-j 1`. Greph gibt keinen Fehler aus, sondern läuft einfach ohne parallele Ausführung.

Du kannst das zur Laufzeit prüfen, indem du `function_exists('pcntl_fork')` überprüfst.

## Hinweise zur Abstimmung

- Setze `-j` zunächst auf die Hälfte deiner physischen Kerne und erhöhe den Wert, bis keine Verbesserungen mehr zu sehen sind.
- Bei reinen Literalsuchen mit Feststrings in kleinen Repositories ist ein einzelner Prozess meist schneller als jede parallele Konfiguration.
- Kein veröffentlichter Benchmark misst parallele AST-Suche oder -Umschreibung. Miss die Leistung in deinem Verzeichnisbaum, bevor du eine Jobanzahl festlegst.
- Die Zeilen „Scan-Modus: Paralleler Text“ in der README (`1`, `2` und `4` Worker auf dem WordPress-Korpus) messen den Pool nicht: Sie suchen nach dem kurzen Literal `function`, dessen Schwellenwert von `jobs * 4_000` Dateien über der Korpusgröße liegt. Daher läuft jede Zeile im selben Prozess ([#8](https://github.com/inline0/greph/issues/8)).

## Einschränkungen

- Der aktuelle Pool unterstützt kein Work-Stealing. Wenn ein Abschnitt deutlich mehr Arbeit verursacht als die anderen, muss der Pool auf den langsamsten Worker warten.
- Die Worker-Ergebnisse werden gepuffert, bis alle Worker fertig sind. Eine Streaming-Ausgabe während eines lang laufenden Scans (wie sie `rg --json` für Ereignisse bietet) wird im parallelen Modus nicht unterstützt.
- Der übergeordnete Prozess sortiert die Ergebnisse nach dem Zusammenführen erneut. Daher verringert der Worker-Pool im Vergleich zu einem Scan in einem einzelnen Prozess nicht den maximalen Speicherbedarf.

Diese Einschränkungen sind für die von Greph abgedeckten Arbeitslasten akzeptabel (interaktive Codesuche, Agentenschleifen, CI-Aufträge mit Momentaufnahmen). Sie werden neu bewertet, wenn Profiling einen Engpass bei realen Korpora zeigt.
