Treci la conținut

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:

Shell
./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).

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.