Przejdź do treści

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:

Powłoka
./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),
);

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_000 plikó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 -j na 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, 2 i 4 procesy robocze w korpusie WordPress) nie mierzą działania puli: wyszukują krótki dosłowny ciąg function, którego próg wynoszący jobs * 4_000 plikó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.