병렬 워커
Greph는 오버헤드를 감수할 만큼 작업량이 충분히 클 때 텍스트 및 AST 스캔을 워커 풀에 분배합니다. 풀은 pcntl_fork()을 기반으로 하며 ext-pcntl을 사용할 수 없는 경우(Windows, 일부 PaaS 환경, 제한된 호스팅) 프로세스 내 실행으로 대체합니다.
병렬 스캔 사용 설정
CLI에서 -j N을 설정하거나 관련 옵션 객체에서 jobs: N를 설정하세요:
./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),
);
병렬 처리가 시작되는 조건
Greph는 무조건 병렬화하지 않습니다. N개 프로세스를 분기하고 파이프를 통해 결과를 주고받는 데 오버헤드가 발생하며 파일 목록이 작으면 그 오버헤드가 검색 자체보다 커집니다. 대신 파사드는 작업 수, 검색 모드 및 패턴 형태를 바탕으로 임계값을 계산합니다. 임계값을 넘는 파일 목록만 워커 풀로 전달됩니다.
현재 휴리스틱(Greph\Greph::shouldUseTextWorkers 등):
- 작업이 하나인 실행(
-j 1)은 항상 프로세스 내에서 실행됩니다. - 기본 텍스트 스캔은 풀을 시작하기 전에 대략
jobs * 2_000개의 파일이 필요합니다. - 개수, 일치 항목이 있는 파일, 일치 항목이 없는 파일 모드에서는 파일별 작업이 더 저렴하므로 임계값이 대략
jobs * 750개로 낮아집니다. - 짧고 식별자 형태인 고정 문자열 패턴은 리터럴 고속 경로가 이미 매우 저렴해 분기해도 이득이 거의 없으므로 임계값을 대략
jobs * 4_000개로 높입니다. - AST 검색 및 AST 재작성에는
jobs * 750임계값을 사용합니다.
이 수치는 보장값이 아닌 휴리스틱입니다. WordPress 벤치마크 코퍼스에 맞춰 조정했으며 향후 변경될 수 있습니다.
풀 작동 방식
Greph\Parallel\WorkSplitter는 파일 목록을 N개 청크로 나눕니다. Greph\Parallel\WorkerPool은 청크마다 워커 프로세스 하나를 분기하고, 각 워커는 자신의 분할에 동일한 클로저를 실행합니다. Greph\Parallel\ResultCollector는 워커마다 한 쌍의 Unix 소켓을 통해 직렬화된 결과를 읽습니다. 부모 프로세스는 모든 워커를 기다린 후 결합된 결과 집합을 병합하고 다시 정렬하므로 작업 분할 방식과 관계없이 출력 순서가 안정적으로 유지됩니다.
텍스트 모드의 워커 풀은 직렬화된 페이로드를 작게 유지하기 위해 맞춤형 전송 중 코덱(Greph\Text\TextResultCodec)을 사용합니다. AST 모드는 PHP의 표준 serialize()를 사용합니다.
pcntl을 사용할 수 없는 경우
pcntl_fork을 호출할 수 없으면(Windows, 보안 강화 PHP 빌드) 풀은 각 청크를 프로세스 내에서 실행하도록 자동으로 전환합니다. 결과는 -j 1과 동일합니다. Greph는 오류를 발생시키지 않고 단일 스레드로 실행합니다.
런타임에 function_exists('pcntl_fork')을 확인해 이를 감지할 수 있습니다.
튜닝 조언
-j을 물리 코어 수의 절반으로 설정하고 개선 효과가 사라질 때까지 늘리세요.- 작은 저장소에서 리터럴 전용 고정 문자열 검색을 할 때는 단일 프로세스가 대개 어떤 병렬 설정을 사용해도 더 빠릅니다.
- 게시된 벤치마크에는 병렬 AST 검색이나 재작성 측정이 없습니다. 작업 수를 정하기 전에 직접 트리에서 측정하세요.
- README의 "스캔 모드: 병렬 텍스트" 행(
1,2및 WordPress 코퍼스에서4개 워커 사용)은 풀을 측정하지 않습니다. 짧은 리터럴function을 검색하며, 이 패턴의 임계값인jobs * 4_000개 파일이 코퍼스 크기보다 많기 때문에 모든 행이 프로세스 내에서 실행됩니다(#8).
제한 사항
- 현재 풀은 작업 훔치기를 지원하지 않습니다. 한 청크가 다른 청크보다 훨씬 무거우면 풀이 가장 느린 워커를 기다립니다.
- 모든 워커가 끝날 때까지 워커 결과가 버퍼링됩니다.
rg --json처럼 장시간 실행되는 스캔에서 이벤트를 스트리밍하는 기능은 병렬 모드에서 지원되지 않습니다. - 부모 프로세스는 결과를 병합한 후 다시 정렬하므로 단일 프로세스 스캔보다 워커 풀이 최대 메모리 사용량을 줄이지는 않습니다.
이러한 제한은 Greph가 대상으로 하는 워크로드(대화형 코드 검색, 에이전트 루프, 스냅샷 대상 CI 작업)에 적합합니다. 실제 코퍼스의 프로파일링에서 병목으로 확인되면 다시 검토합니다.