2013-08-15 11 views
5

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:

  1. Równolegle, kompilowania i trawienie regexes , ~ 2 secs
  2. 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.

+0

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

+0

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.) –

+0

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

Odpowiedz

0

Aż Kocham pytona, myślę, że rozwiązaniem jest zrobić to w Perl (see this speed comparison, for example) lub C, itd

Jeśli chcesz zachować główny program w Pythonie, można użyć subprocess aby wywołać skrypt perl (pamiętaj, aby maksymalnie ograniczyć liczbę możliwych wywołań w możliwie jak najmniejszej liczbie subprocess, aby uniknąć narzutu.)

Powiązane problemy