
# Паралелни работници

Greph разпределя текстовите и AST сканиранията между пул от работници, когато обемът работа е достатъчен, за да оправдае разходите. Пулът е базиран на `pcntl_fork()` и преминава към изпълнение в процеса, когато `ext-pcntl` не е наличен (Windows, някои PaaS среди, ограничен хостинг).

## Активиране на паралелни сканирания

Задайте `-j N` на CLI или `jobs: N` в съответния обект с опции:

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

## Кога се включва паралелната обработка

Greph не изпълнява всичко паралелно безусловно. Стартирането на N процеса и обменът на резултатите им чрез канали създават разходи, които при малки списъци с файлове надвишават цената на самото търсене. Вместо това фасадата изчислява праг според броя задачи, режима на търсене и формата на шаблона. Само списъците с файлове над прага се изпращат към пула от работници.

Текущите евристики (в `Greph\Greph::shouldUseTextWorkers` и свързаните с него компоненти):

- Изпълненията с една задача (`-j 1`) винаги се изпълняват в процеса.
- Обикновените текстови сканирания изискват приблизително `jobs * 2_000` файла, преди да се включи пулът.
- Режимите за брой, файлове със съвпадения и файлове без съвпадения понижават прага до около `jobs * 750`, защото обработката на файл е по-евтина.
- Кратките шаблони с фиксиран низ във формата на идентификатор повишават прага до около `jobs * 4_000`, защото бързият път за буквални низове вече е толкова евтин, че стартирането на процеси рядко си струва.
- Търсенето и замяната в AST използват прага `jobs * 750`.

Тези числа са евристични, а не гарантирани стойности. Те са настроени спрямо бенчмарк корпуса WordPress и може да бъдат преразгледани.

## Как работи пулът

`Greph\Parallel\WorkSplitter` разпределя списъка с файлове между N части. `Greph\Parallel\WorkerPool` стартира отделен работен процес за всяка част; всеки работник изпълнява същата функция върху своя дял, а `Greph\Parallel\ResultCollector` прочита сериализираните резултати през отделна двойка сокети Unix за всеки работник. Родителският процес изчаква всички работници, след което обединява и сортира отново общия набор от резултати, така че редът на изхода да е стабилен независимо от разпределението на работата.

За текстовия режим пулът от работници използва персонализиран кодек за предаване по мрежата (`Greph\Text\TextResultCodec`), за да запази сериализирания полезен товар компактен. Режимът AST използва стандартния `serialize()` на PHP.

## Когато pcntl не е наличен

Ако `pcntl_fork` не може да бъде извикана (Windows, компилации на PHP с усилена защита), пулът прозрачно преминава към изпълнение на всяка част в процеса. Резултатът е същият като при `-j 1`. Greph не извежда грешка, а просто се изпълнява в една нишка.

Можете да проверите това по време на изпълнение с `function_exists('pcntl_fork')`.

## Съвети за настройване

- Започнете със `-j`, зададен на половината от броя на физическите ядра, и го увеличавайте, докато подобренията спрат.
- При търсене с фиксиран низ в малки хранилища един процес обикновено е по-бърз от всяка паралелна настройка.
- Няма публикуван бенчмарк за паралелно търсене или замяна в AST. Измерете производителността върху собственото си дърво, преди да изберете броя задачи.
- Редовете „Режим на сканиране: паралелен текст“ в README (работници: `1`, `2` и `4`, корпус WordPress) не измерват пула: търсят краткия буквален низ `function`, чийто праг от `jobs * 4_000` файла е над размера на корпуса, така че всеки ред се изпълнява в процеса ([#8](https://github.com/inline0/greph/issues/8)).

## Ограничения

- Пулът понастоящем не поддържа разпределяне на работа от неангажирани работници. Ако една част е значително по-тежка от останалите, пулът изчаква най-бавния работник.
- Резултатите от работниците се буферират, докато всички приключат. Паралелният режим не поддържа поточно извеждане от продължително сканиране (както `rg --json` предава събития).
- След обединяването родителският процес сортира резултатите отново, затова пулът от работници не намалява пиковото използване на паметта спрямо сканирането в един процес.

Тези ограничения са приемливи за работните натоварвания, към които е насочен Greph (интерактивно търсене в код, цикли на агенти, CI задачи върху моментни снимки). Ще бъдат преразгледани, ако профилирането покаже, че са пречка при реални корпуси.
