7

Pracowałem nad aplikacją na gae używając Pythona 2.7, wywołanie ajaxa wymaga pewnych danych z API, pojedyncze żądanie może zająć ~ 200 ms, jednak gdy otworzę dwie przeglądarki i zrobię dwa Żądania w bardzo krótkim czasie zabierają więcej niż podwójne, próbowałem umieszczać wszystko w wątkach, ale to nie działało. (dzieje się tak, gdy aplikacja jest w trybie online, nie tylko na serwerze-dev)Paralelizm w Pythonie nie działa poprawnie

Więc napisałem ten prosty test, aby sprawdzić, czy jest to problem w Pythonie w ogóle (w przypadku męczącym oczekiwaniu), tutaj jest kod, a wynik:

def work(): 
    t = datetime.now() 
    print threading.currentThread(), t 
    i = 0 
    while i < 100000000: 
     i+=1 
    t2 = datetime.now() 
    print threading.currentThread(), t2, t2-t 

if __name__ == '__main__': 
    print "single threaded:" 
    t1 = threading.Thread(target=work) 
    t1.start() 
    t1.join() 

    print "multi threaded:" 
    t1 = threading.Thread(target=work) 
    t1.start() 
    t2 = threading.Thread(target=work) 
    t2.start() 
    t1.join() 
    t2.join() 

wynik na mac oS X do ore i7 (4 rdzenie, 8 wątków), python2.7:

single threaded: 
<Thread(Thread-1, started 4315942912)> 2011-12-06 15:38:07.763146 
<Thread(Thread-1, started 4315942912)> 2011-12-06 15:38:13.091614 0:00:05.328468 

multi threaded: 
<Thread(Thread-2, started 4315942912)> 2011-12-06 15:38:13.091952 
<Thread(Thread-3, started 4323282944)> 2011-12-06 15:38:13.102250 
<Thread(Thread-3, started 4323282944)> 2011-12-06 15:38:29.221050 0:00:16.118800 
<Thread(Thread-2, started 4315942912)> 2011-12-06 15:38:29.237512 0:00:16.145560 

To jest dość szokujące! jeśli pojedynczy wątek zajęłby 5 sekund, aby to zrobić .. Myślałem, że rozpoczęcie dwóch wątków w tym samym czasie zajmie tyle samo czasu, aby ukończyć oba zadania, ale zajmuje to prawie trzy razy więcej czasu. To sprawia, że ​​cały wątek jest bezużyteczny, ponieważ szybciej byłoby je wykonać sekwencyjnie!

co jestem tutaj brakuje ..

+3

Czy czytasz cokolwiek o Globalnej Blokadzie Interpretera (GIL) w Pythonie? Jeśli chcesz równoległe przetwarzanie, powinieneś spojrzeć na proces wieloprocesowy, a nie na wątki. Wykonanie jest ograniczone do jednego wątku na raz, chyba że biblioteki, z którymi pracujesz, są specjalnie zaprojektowane do wydania GIL. –

+2

Twój benchmark jest źle zaprojektowany.Twój rzeczywisty przypadek użycia będzie związany z IO, a nie związany z CPU. Python's GIL zachowuje się zupełnie inaczej w każdym przypadku. Nawlekanie * powinno * działać prawidłowo dla ciebie w rzeczywistym przypadku. – zeekay

+1

@ g.d.d.c. multiprocessing nie jest dostępny w GAE – bpgergo

Odpowiedz

9

David Beazley gave a talk o tym numerze na PyCon 2010. Jak już powiedzieli inni, w przypadku niektórych zadań korzystanie z wątków, szczególnie w przypadku wielu rdzeni, może prowadzić do spowolnienia działania w porównaniu z tym samym zadaniem wykonywanym przez pojedynczy wątek. Problem Beazley znaleziony, miał do czynienia z wieloma rdzeniami o "GIL battle":

enter image description here

Aby uniknąć GIL twierdzenie, można uzyskać lepsze wyniki zawierające zadania uruchamiane w osobnych procesach zamiast oddzielnych wątkach. Moduł multiprocessing zapewnia wygodny sposób, aby to zrobić, zwłaszcza, że ​​interfejs API do obsługi wielu formatów jest bardzo podobny do interfejsu API dla wątków.

import multiprocessing as mp 
import datetime as dt 
def work(): 
    t = dt.datetime.now() 
    print mp.current_process().name, t 
    i = 0 
    while i < 100000000: 
     i+=1 
    t2 = dt.datetime.now() 
    print mp.current_process().name, t2, t2-t 

if __name__ == '__main__': 
    print "single process:" 
    t1 = mp.Process(target=work) 
    t1.start() 
    t1.join() 

    print "multi process:" 
    t1 = mp.Process(target=work) 
    t1.start() 
    t2 = mp.Process(target=work) 
    t2.start() 
    t1.join() 
    t2.join() 

daje

single process: 
Process-1 2011-12-06 12:34:20.611526 
Process-1 2011-12-06 12:34:28.494831 0:00:07.883305 
multi process: 
Process-3 2011-12-06 12:34:28.497895 
Process-2 2011-12-06 12:34:28.503433 
Process-2 2011-12-06 12:34:36.458354 0:00:07.954921 
Process-3 2011-12-06 12:34:36.546656 0:00:08.048761 

PS. Jak zauważyło zeekay w komentarzach, bitwa GIL jest tylko poważna dla zadań związanych z CPU. To nie powinno być problemem dla zadań związanych z IO.

+0

Czy to zachowanie jest specyficzne dla Pythona? A jeśli tak, dlaczego? –

+1

GIL jest specyficzny dla Pythona, więc "GIL bitwy" są specyficzne dla GIL. Nie jestem pewien, czy coś podobnego może się zdarzyć w innych językach. – unutbu

1

chciałbym spojrzeć na którym czas się dzieje. Załóżmy na przykład, że serwer może odpowiadać tylko na jedno zapytanie co 200ms. Wtedy nic nie możesz zrobić, dostaniesz tylko jedną odpowiedź co 200 ms, ponieważ to wszystko serwer może ci zapewnić.

+0

Spójrz na jego kod. Wzywa 'datetime', nie komunikuje się z serwerem. –