Równoległe procesy robocze
Greph rozdziela skanowanie tekstu i AST między procesy robocze, gdy ilość pracy uzasadnia związane z tym koszty. Pula korzysta z pcntl_fork() i przechodzi na wykonywanie w bieżącym procesie, gdy ext-pcntl nie jest dostępne (Windows, niektóre środowiska PaaS, hosting z ograniczeniami).
Włączanie skanowania równoległego
Ustaw -j N w CLI albo jobs: N w odpowiednim obiekcie opcji:
./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),
);
Kiedy uruchamia się tryb równoległy
Greph nie działa równolegle bezwarunkowo. Uruchamianie N procesów i przesyłanie wyników w obie strony przez potoki wiąże się z narzutem, który przy małych listach plików przewyższa koszt samego wyszukiwania. Zamiast tego fasada oblicza próg na podstawie liczby zadań, trybu wyszukiwania i kształtu wzorca. Do puli trafiają tylko listy plików przekraczające ten próg.
Obecne heurystyki (w Greph\Greph::shouldUseTextWorkers i powiązanych elementach):
- Uruchomienia z jednym zadaniem (
-j 1) zawsze odbywają się w bieżącym procesie. - Domyślne skanowanie tekstu wymaga około
jobs * 2_000plików, aby uruchomić pulę. - Tryby zliczania, plików z dopasowaniami i plików bez dopasowań obniżają próg do około
jobs * 750, ponieważ praca na plik jest tańsza. - Krótkie wzorce dokładnych ciągów przypominające identyfikatory podnoszą próg do około
jobs * 4_000, ponieważ szybka ścieżka dopasowań dosłownych jest już tak tania, że uruchamianie procesów rzadko się opłaca. - Wyszukiwanie i przepisywanie AST korzystają z progu
jobs * 750.
To wartości heurystyczne, a nie gwarancje. Są dostrojone do korpusu testów wydajnościowych WordPress i mogą zostać zmienione.
Jak działa pula
Greph\Parallel\WorkSplitter dzieli listę plików na N porcji. Greph\Parallel\WorkerPool uruchamia oddzielny proces roboczy dla każdej porcji; każdy proces wykonuje tę samą funkcję na przypisanym mu fragmencie, a Greph\Parallel\ResultCollector odczytuje zaserializowane wyniki przez osobną parę gniazd Unix dla każdego procesu. Proces nadrzędny czeka na zakończenie wszystkich procesów, a następnie scala i ponownie sortuje wyniki, dzięki czemu ich kolejność jest stabilna niezależnie od podziału pracy.
W trybie tekstowym pula procesów roboczych korzysta z niestandardowego kodera przewodowego (Greph\Text\TextResultCodec), aby zmniejszyć rozmiar serializowanych danych. Tryb AST używa standardowego serialize() należącego do PHP.
Gdy pcntl jest niedostępne
Jeśli pcntl_fork nie jest dostępne do wywołania (Windows, kompilacje PHP z zaostrzonymi zabezpieczeniami), pula bezproblemowo przechodzi na wykonywanie każdej porcji w bieżącym procesie. Wynik jest taki sam jak w -j 1. Greph nie zgłasza błędu, tylko działa jednowątkowo.
Możesz wykryć taką sytuację w czasie wykonywania, sprawdzając function_exists('pcntl_fork').
Wskazówki dotyczące dostrajania
- Zacznij od ustawienia
-jna połowę liczby fizycznych rdzeni i zwiększaj wartość, aż przestaniesz zauważać poprawę. - W małych repozytoriach wyszukiwanie oparte wyłącznie na dokładnych ciągach zwykle działa szybciej w jednym procesie niż przy dowolnej konfiguracji równoległej.
- Żaden opublikowany test wydajnościowy nie mierzy równoległego wyszukiwania ani przepisywania AST. Zmierz wydajność na własnym drzewie przed ustaleniem liczby zadań.
- Wiersze „Tryb skanowania: tekst równoległy” w README (
1,2i4procesy robocze w korpusie WordPress) nie mierzą działania puli: wyszukują krótki dosłowny ciągfunction, którego próg wynoszącyjobs * 4_000plików przekracza rozmiar korpusu, więc każdy wiersz działa w bieżącym procesie (#8).
Ograniczenia
- Obecna pula nie obsługuje równoważenia obciążenia przez przekazywanie zadań. Jeśli jedna porcja jest znacznie cięższa od pozostałych, pula czeka na najwolniejszy proces.
- Wyniki procesów roboczych są buforowane do czasu zakończenia wszystkich. Tryb równoległy nie obsługuje strumieniowego wyjścia z długotrwałego skanowania, tak jak robi to
rg --json, emitując zdarzenia. - Proces nadrzędny ponownie sortuje wyniki po scaleniu, więc pula procesów roboczych nie zmniejsza maksymalnego zużycia pamięci w porównaniu ze skanowaniem w jednym procesie.
Te ograniczenia są akceptowalne dla obciążeń, na które ukierunkowano Greph: interaktywne wyszukiwanie kodu, pętle agentów i zadania CI na migawkach. Zostaną ponownie ocenione, jeśli profilowanie wykaże, że stanowią wąskie gardło w rzeczywistych korpusach.