Worker paralleli
Greph distribuisce le scansioni di testo e AST su un pool di worker quando il carico giustifica il sovraccarico. Il pool si basa su pcntl_fork() e passa all'esecuzione in-process se ext-pcntl non è disponibile (Windows, alcuni ambienti PaaS, hosting con restrizioni).
Abilitare le scansioni parallele
Imposta -j N su CLI oppure jobs: N nell'oggetto delle opzioni pertinente:
./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
use Greph\Greph;
use Greph\Text\TextSearchOptions;
Greph::searchText(
'function',
'src',
new TextSearchOptions(fixedString: true, jobs: 8),
);
Quando si attiva l'esecuzione parallela
Greph non parallelizza incondizionatamente. Avviare N processi e scambiare i risultati tramite pipe ha un costo che, con elenchi di file ridotti, supera quello della ricerca stessa. Per questo la facade calcola una soglia in base al numero di job, alla modalità di ricerca e alla forma del pattern. Solo gli elenchi di file che superano la soglia vengono inviati al pool di worker.
Le euristiche attuali (in Greph\Greph::shouldUseTextWorkers e strumenti correlati):
- Le esecuzioni con un solo job (
-j 1) avvengono sempre in-process. - Le scansioni di testo predefinite richiedono circa
jobs * 2_000file prima che si attivi il pool. - Le modalità conteggio, file con corrispondenze e file senza corrispondenze abbassano la soglia a circa
jobs * 750, perché il lavoro per file è meno costoso. - I pattern a stringa fissa brevi, simili a identificatori, alzano la soglia a circa
jobs * 4_000, perché il percorso rapido per i letterali è già così economico che avviare processi raramente conviene. - La ricerca e la riscrittura AST usano la soglia
jobs * 750.
Questi numeri sono euristiche, non garanzie. Sono tarati sul corpus di benchmark WordPress e possono essere rivisti.
Funzionamento del pool
Greph\Parallel\WorkSplitter suddivide l'elenco dei file in N blocchi. Greph\Parallel\WorkerPool avvia un processo worker per ogni blocco; ogni worker esegue la stessa closure sulla propria porzione e Greph\Parallel\ResultCollector legge i risultati serializzati tramite una coppia di socket Unix per worker. Il processo padre attende tutti i worker, quindi unisce e riordina i risultati combinati, così l'ordine dell'output rimane stabile indipendentemente dalla suddivisione del lavoro.
Per la modalità testo, il pool di worker usa un codec personalizzato wire (Greph\Text\TextResultCodec) per mantenere compatto il payload serializzato. La modalità AST usa il formato standard serialize() di PHP.
Quando pcntl non è disponibile
Se pcntl_fork non è richiamabile (Windows, build PHP rinforzate), il pool passa automaticamente all'esecuzione di ogni blocco in-process. Il risultato è lo stesso di -j 1. Greph non genera errori: esegue semplicemente un thread alla volta.
Puoi rilevarlo in fase di esecuzione controllando function_exists('pcntl_fork').
Consigli per la configurazione
- Inizia impostando
-ja metà del numero di core fisici e aumentalo finché non smetti di vedere miglioramenti. - Per ricerche a stringa fissa solo letterali in repository piccoli, un singolo processo è in genere più veloce di qualsiasi configurazione parallela.
- Nessun benchmark pubblicato misura la ricerca o la riscrittura AST parallele. Misura le prestazioni sul tuo albero prima di scegliere il numero di job.
- Le righe «Modalità di scansione: testo parallelo» del benchmark README (
1,2e4worker sul corpus WordPress) non misurano il pool: cercano il breve letteralefunction, la cui soglia dijobs * 4_000file supera le dimensioni del corpus, quindi ogni riga viene eseguita in-process (#8).
Limitazioni
- Il pool attuale non supporta il work-stealing. Se un blocco è molto più impegnativo degli altri, il pool attende il worker più lento.
- I risultati dei worker vengono memorizzati in buffer fino al termine di tutti i worker. In modalità parallela non è supportato lo streaming dell'output da una scansione di lunga durata (come fa
rg --jsoncon gli eventi). - Dopo l'unione, il processo padre riordina i risultati: rispetto a una scansione con un solo processo, il pool di worker non riduce quindi il picco di memoria utilizzata.
Queste limitazioni sono accettabili per i carichi di lavoro a cui mira Greph (ricerca interattiva nel codice, cicli degli agenti, job CI su snapshot). Verranno rivalutate se la profilazione mostra che rappresentano un collo di bottiglia su corpus reali.