
# العمال المتوازيون

يوزّع 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 وإعادة كتابة 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` و`4` عمال على مجموعة البيانات WordPress) لا تقيس التجمع: فهي تبحث عن السلسلة الحرفية القصيرة `function`، وعتبتها البالغة `jobs * 4_000` ملفًا تتجاوز حجم مجموعة البيانات؛ لذلك تعمل كل الصفوف داخل عملية واحدة ([#8](https://github.com/inline0/greph/issues/8)).

## القيود

- لا يدعم التجمع حاليًا توزيع العمل أثناء التنفيذ. إذا كان أحد الأجزاء أثقل بكثير من البقية، فسيتعين على التجمع انتظار أبطأ عامل.
- تُخزّن نتائج العمال مؤقتًا حتى ينتهي الجميع. لا يدعم الوضع المتوازي تدفق مخرجات الفحص طويل التشغيل بالطريقة التي يبث بها `rg --json` الأحداث.
- تعيد العملية الأم فرز النتائج بعد دمجها، لذلك لا يقلل تجمع العمال ذروة استخدام الذاكرة مقارنة بالفحص بعملية واحدة.

هذه القيود مقبولة لأعباء العمل التي يستهدفها Greph، مثل البحث التفاعلي في الأكواد ودورات عمل الوكلاء ومهام CI على اللقطات. وسيعاد النظر فيها إذا أظهر تحليل الأداء أنها تسبب اختناقًا في مجموعات بيانات واقعية.
