Pular para o conteúdo

Workers paralelos

Greph distribui as varreduras de texto e AST por um conjunto de workers quando o volume de trabalho justifica a sobrecarga. O conjunto usa pcntl_fork() e recorre à execução no próprio processo quando ext-pcntl não está disponível (Windows, alguns ambientes PaaS, hospedagem restrita).

Ativar varreduras paralelas

Defina -j N em CLI ou jobs: N no objeto de opções relevante:

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

Quando o paralelismo é ativado

Greph não paraleliza incondicionalmente. Criar N processos e transferir os resultados deles por pipes gera sobrecarga que, em listas pequenas de arquivos, supera o custo da própria pesquisa. Em vez disso, a fachada calcula um limite com base no número de tarefas, no modo de pesquisa e no formato do padrão. Somente listas de arquivos acima do limite são distribuídas ao conjunto de workers.

As heurísticas atuais (em Greph\Greph::shouldUseTextWorkers e similares):

  • Execuções com uma tarefa (-j 1) sempre ocorrem no próprio processo.
  • Varreduras de texto padrão exigem cerca de jobs * 2_000 arquivos para ativar o conjunto.
  • Os modos de contagem, arquivos com correspondências e arquivos sem correspondências reduzem o limite para cerca de jobs * 750, pois o trabalho por arquivo é mais barato.
  • Padrões curtos de cadeia fixa em formato de identificador aumentam o limite para cerca de jobs * 4_000, pois o caminho rápido literal já é tão barato que raramente compensa criar processos.
  • A pesquisa e a reescrita de AST usam o limite jobs * 750.

Esses números são heurísticas, não garantias. São ajustados com base no conjunto de benchmarks WordPress e podem ser revistos.

Como o conjunto funciona

Greph\Parallel\WorkSplitter distribui a lista de arquivos em N partes. Greph\Parallel\WorkerPool cria um processo worker por parte; cada worker executa o mesmo closure na sua fatia; e Greph\Parallel\ResultCollector lê os resultados serializados por um par de sockets Unix para cada worker. O processo pai aguarda todos os workers e, em seguida, combina e reordena os resultados para manter a ordem da saída estável, independentemente de como o trabalho foi dividido.

No modo de texto, o conjunto de workers usa um codec personalizado para transmissão (Greph\Text\TextResultCodec) para manter compacto o conteúdo serializado. O modo AST usa o serialize() padrão de PHP.

Quando pcntl não está disponível

Se pcntl_fork não puder ser chamado (Windows, compilações PHP reforçadas), o conjunto recorre de forma transparente à execução de cada parte no próprio processo. O resultado é o mesmo que em -j 1. Greph não gera erro; simplesmente executa em uma única thread.

Você pode detectar isso em tempo de execução verificando function_exists('pcntl_fork').

Recomendações de ajuste

  • Comece com -j definido como metade do número de núcleos físicos e aumente até deixar de ver melhorias.
  • Para pesquisas somente literais por cadeia fixa em repositórios pequenos, um único processo costuma ser mais rápido do que qualquer configuração paralela.
  • Nenhum benchmark publicado mede a pesquisa ou reescrita paralela de AST. Faça medições na sua própria árvore antes de definir o número de tarefas.
  • As linhas "Modo de varredura: texto paralelo" do README (1, 2 e 4 workers no conjunto WordPress) não medem o conjunto de workers: elas pesquisam o literal curto function, cujo limite de jobs * 4_000 arquivos supera o tamanho do conjunto. Por isso, todas as linhas são executadas no próprio processo (#8).

Limitações

  • O conjunto atual não oferece roubo de tarefas. Se uma parte for muito mais pesada que as demais, o conjunto aguardará o worker mais lento.
  • Os resultados dos workers ficam em buffer até que todos terminem. A saída em fluxo de uma varredura longa (como os eventos transmitidos por rg --json) não é compatível com o modo paralelo.
  • O processo pai reordena os resultados depois de combiná-los, portanto o conjunto de workers não reduz o pico de uso de memória em comparação com uma varredura em processo único.

Essas limitações são aceitáveis para as cargas de trabalho visadas por Greph (pesquisa interativa de código, ciclos dos agentes, tarefas de CI em snapshots). Serão revistas se a criação de perfis mostrar que são um gargalo em conjuntos de dados reais.