
# Lucrători paraleli

Greph distribuie scanările text și AST într-un grup de procese de lucru atunci când volumul justifică costul suplimentar. Grupul se bazează pe `pcntl_fork()` și revine la execuția în procesul curent când `ext-pcntl` nu este disponibil (Windows, unele medii PaaS, găzduire cu restricții).

## Activarea scanărilor paralele

Setați `-j N` pentru CLI sau `jobs: N` în obiectul de opțiuni relevant:

```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),
);
```

## Când începe execuția în paralel

Greph nu rulează întotdeauna în paralel. Pornirea a N procese și schimbul rezultatelor prin pipe-uri implică un cost suplimentar, iar pentru liste mici de fișiere acesta depășește costul căutării. În schimb, fațada calculează un prag în funcție de numărul de sarcini, modul de căutare și forma modelului. Doar listele de fișiere care depășesc pragul sunt distribuite grupului de procese de lucru.

euristicile actuale (din `Greph\Greph::shouldUseTextWorkers` și instrumentele conexe):

- Rulările cu o singură sarcină (`-j 1`) se execută întotdeauna în procesul curent.
- Scanările text implicite necesită aproximativ `jobs * 2_000` fișiere înainte ca grupul să fie activat.
- Modurile de numărare, fișiere cu potriviri și fișiere fără potriviri reduc pragul la aproximativ `jobs * 750`, deoarece operația per fișier este mai ieftină.
- Modelele scurte cu șir fix, de forma identificatorilor, măresc pragul la aproximativ `jobs * 4_000`, deoarece calea rapidă literală este deja atât de ieftină încât pornirea proceselor rareori merită.
- Căutarea și rescrierea AST folosesc pragul `jobs * 750`.

Aceste valori sunt euristici, nu promisiuni. Sunt ajustate pe baza corpusului de testare WordPress și pot fi revizuite.

## Cum funcționează grupul de procese

`Greph\Parallel\WorkSplitter` distribuie lista de fișiere în N segmente. `Greph\Parallel\WorkerPool` pornește câte un proces de lucru pentru fiecare segment; fiecare proces rulează aceeași funcție pe porțiunea alocată, iar `Greph\Parallel\ResultCollector` citește rezultatele serializate prin câte o pereche de socket-uri Unix pentru fiecare proces. Procesul părinte așteaptă terminarea tuturor proceselor, apoi îmbină și sortează din nou rezultatele, astfel încât ordinea ieșirii să rămână stabilă indiferent de împărțirea sarcinii.

Pentru modul text, grupul de procese folosește un codec personalizat pentru transmiterea datelor (`Greph\Text\TextResultCodec`), pentru a păstra compactă încărcătura serializată. Modul AST folosește PHP `serialize()` standard.

## Când pcntl nu este disponibil

Dacă `pcntl_fork` nu poate fi apelat (Windows, compilări PHP securizate), grupul revine automat la execuția fiecărui segment în procesul curent. Rezultatul este același ca pentru `-j 1`. Greph nu generează o eroare; rulează pur și simplu pe un singur fir de execuție.

Puteți detecta acest lucru în timpul execuției verificând `function_exists('pcntl_fork')`.

## Recomandări de ajustare

- Începeți cu `-j` setat la jumătate din numărul de nuclee fizice și măriți valoarea până când nu mai observați îmbunătățiri.
- Pentru căutările exclusiv literale cu șir fix în depozite mici, un singur proces este de obicei mai rapid decât orice configurație paralelă.
- Niciun test de performanță publicat nu măsoară căutarea sau rescrierea AST în paralel. Măsurați pe propriul arbore înainte de a stabili numărul de sarcini.
- Rândurile „Modul de scanare: text paralel” din README (`1`, `2` și `4` procese de lucru pe corpusul WordPress) nu măsoară grupul de procese: caută literalul scurt `function`, al cărui prag de `jobs * 4_000` fișiere depășește dimensiunea corpusului, așadar fiecare rând rulează în procesul curent ([#8](https://github.com/inline0/greph/issues/8)).

## Limitări

- Grupul actual nu acceptă distribuirea dinamică a sarcinilor. Dacă un segment este mult mai greu decât celelalte, grupul așteaptă cel mai lent proces de lucru.
- Rezultatele proceselor de lucru sunt păstrate în memorie până când toate se termină. Modul paralel nu acceptă transmiterea treptată a ieșirii unei scanări de lungă durată, așa cum `rg --json` transmite evenimente.
- Procesul părinte sortează din nou rezultatele după îmbinare, astfel încât grupul de procese nu reduce consumul maxim de memorie față de o scanare cu un singur proces.

Aceste limitări sunt acceptabile pentru sarcinile vizate de Greph (căutare interactivă în cod, bucle de agent, joburi CI pe instantanee). Le vom reevalua dacă profilarea arată că devin un blocaj în corpusuri reale.
