Mam program, w którym muszę skompilować kilka tysięcy dużych wyrażeń regularnych, z których wszystkie będą używane wielokrotnie. Problem polega na tym, że trwa to zbyt długo (zgodnie z cProfiler
, 113 s) do re.compile()
im. (BTW, rzeczywiście szukają przy wszystkich tych regexes < 1,3 sek po zebranych.)Python: Kompilowanie wyrażeń regularnych równolegle
Jeśli nie precompile, to tylko odkłada problem do kiedy rzeczywiście szukać, ponieważ re.search(expr, text)
niejawnie kompiluje expr
. W rzeczywistości jest gorzej, ponieważ re
będzie przekompilował całą listę wyrażeń regularnych za każdym razem, gdy ich używam.
Próbowałem użyć multiprocessing
, ale to faktycznie spowalnia rzeczy. Oto mały test, aby wykazać, że:
## rgxparallel.py ##
import re
import multiprocessing as mp
def serial_compile(strings):
return [re.compile(s) for s in strings]
def parallel_compile(strings):
print("Using {} processors.".format(mp.cpu_count()))
pool = mp.Pool()
result = pool.map(re.compile, strings)
pool.close()
return result
l = map(str, xrange(100000))
i mój skrypt testowy:
#!/bin/sh
python -m timeit -n 1 -s "import rgxparallel as r" "r.serial_compile(r.l)"
python -m timeit -n 1 -s "import rgxparallel as r" "r.parallel_compile(r.l)"
# Output:
# 1 loops, best of 3: 6.49 sec per loop
# Using 4 processors.
# Using 4 processors.
# Using 4 processors.
# 1 loops, best of 3: 9.81 sec per loop
Zgaduję, że wersja równoległy:
- Równolegle, kompilowania i trawienie regexes , ~ 2 secs
- W trybie szeregowym, nieopróżnianie, a zatem ponowne skompilowanie ich wszystkich, ~ 6,5 s.
Wraz z narzutem do włączania i wyłączania tych procesów multiprocessing
na czterech procesorów jest więcej niż 25% wolniej niż seryjny.
Próbowałem także podzielić listę wyrażeń na 4 podkategorie i pool.map
- na podlisty, a nie na poszczególne wyrażenia. To dało niewielki wzrost wydajności, ale nadal nie mogłem być lepszy niż ~ 25% wolniej niż seryjny.
Czy istnieje sposób na szybszą kompilację niż seryjny?
EDYCJA: Poprawiono czas działania kompilacji regex.
Próbowałem również używać threading
, ale ze względu na GIL użyto tylko jednego procesora. Był nieco lepszy niż multiprocessing
(130 sekund vs. 136 sekund), ale nadal wolniejszy niż seryjny (113 sekund).
EDYCJA 2: Uświadomiłem sobie, że niektóre wyrażenia były prawdopodobnie duplikowane, więc dodałem do nich polecenie, aby je zapisać. Ogolenie się to ~ 30 sekund. Nadal jednak interesuję się paralelizacją. Maszyna docelowa ma 8 procesorów, co skróci czas kompilacji do ~ 15 sekund.
Dlaczego masz tak wiele wyrażeń regularnych i nie robisz tak niewiele przy ich wyszukiwaniu? Czy możesz je uprościć, być może zastąpić je zwykłą starą manipulacją strunami lub w ogóle ich nie uruchamiać? – delnan
Czas wyszukiwania polega na jednorazowym użyciu całej listy. Bardzo ważne jest, aby czas pojedynczego wyszukiwania list był niewielki, ponieważ użytkownik (i mój pracodawca) będzie oczekiwał natychmiastowej reakcji. Spróbowałem uprościć tyle, ile mogłem, i to jest najlepsze, co mogę osiągnąć bez wycinania najważniejszych funkcji. (Rzeczywista lista wyszukiwanych haseł to ~ 200 000 pozycji, mam kod, który przełącza się na proste funkcje łańcuchowe, gdy tylko jest to możliwe, ale to wciąż pozostawia ~ 5 000 wyrażeń regularnych.) –
Czy próbowałeś używać wątków zamiast? 1 wątek na procesor i regex's podzielone między nimi? regex jest implementowany w C, więc powinieneś uzyskać przyzwoity poziom paralelizmu pomimo GIL. – tdelaney