with open("scan/scan.txt") as file:
for line in file:
check(line)
Problem jest taki ze plik ma ok 50000 linijek.
Jak można usprawnić ten kod zeby działał szybciej? Multiprocessing? Nigdy nie robiłem programu który wykorzystuje multi threading-processing.
Ile teraz trwa wykonanie tego kodu a ile uważasz że powinno? Co robi funkcja check? Czy blokuje ją wejście/wyjście czy obliczenia? Czy zastosowany algorytm jest na pewno optymalny? Bez znajomości problemu nic tak naprawdę nie można powiedzieć.
Nie wiem, co robi Twój check, ale być może wykorzystanie map w miejscu iteracji po wierszach pliku pozwoli Ci przyspieszyć wykonanie.
Poniżej prosty przykład z rezultatami pomiarów które walnąłem na szybko - map wykonał się znacznie szybciej niż iteracja czy list comprehension:
>>> from time import time
>>>
>>> collection = ["A" * 1000 for _ in range(50000)]
>>>
>>> def my_procedure(line):
... long_line = line * 10
... half_of_long_line = long_line[:len(long_line)//2]
... return len(half_of_long_line) > 10000
...
>>> def as_map(procedure):
... return map(procedure, collection)
...
>>> def as_comprehension(procedure):
... return [procedure(line) for line in collection]
...
>>> def as_loop(procedure):
... result = []
... for line in collection:
... result.append(procedure(line))
... return result
...
>>> def timeit(fun):
... start = time()
... for _ in range(10):
... result = fun(my_procedure)
... end = time()
... avg_time = (end - start)/10.0
... print("Average time for {0}, 10 runs: {1:10.6f}s".format(fun.__name__, avg_time))
...
>>> timeit(as_map)
Average time for as_map, 10 runs: 0.000098s
>>> timeit(as_loop)
Average time for as_loop, 10 runs: 0.048471s
>>> timeit(as_comprehension)
Average time for as_comprehension, 10 runs: 0.046080s
Swoją drogą, jeśli chcesz wykorzystać multiprocessing lub multithreading to dorzucę jeszcze trzy grosze w temacie:
multithreading w Pythonie jest często gęsto mało efektywny, a wszystko z powodu wuja GIL-a, który ratuje programistę przed skutkami zarządzania pamięcią przez CPython, które nie jest thread-safe
możesz wykorzystać zamiast tego moduł multiprocessing i pulę procesów multiprocessing.Pool. Pool ma metodę map, która pozwala dość wygodnie przetwarzać listy itp. z wykorzystaniem puli procesów, pozwalając ominąć GIL a jednocześnie nie babrać się z zarządzaniem procesami. Wada jest taka, że moduł multiprocessing - przynajmniej z mojego osobistego doświadczenia - jest mocno upośledzony, jeśli chodzi o obsługę błędów. Co się przekopałem przez wątki na SO, które traktują o omijaniu jego ograniczeń jeśli chodzi o obsługę sytuacji, gdy np. proces potomny wywali się z powodu wyjątku czy SIGINT, to moje.
alternatywą, moim zdaniem lepszą od multiprocessing, jest moduł concurrent, który ma własną pulę procesów: concurrent.futures.ProcessPoolExecutor, która generalnie lepiej sobie radzi z przypadkami, gdy proces-dziecko umrze. Jest jeszcze kwestia tego co się stanie, gdy zabity zostanie proces-rodzic, ale z tym akurat ciężko cokolwiek zrobić (chyba, że zrobisz jakiś awaryjny mechanizm ubijający pulę w procesie-rodzicu, na przykład gdy proces dostanie sygnał SIGINT / SIGABRT poprzez moduł signal, ale na SIGKILL już nic nie poradzisz).
możesz poczytać sobie o modułach numba i dask - pierwszy pozwala na kompilację JIT wybranych fragmentów kodu pisanego w Pythonie i tym samym przyspieszenie wykonania, drugi na równoległe czy nawet rozproszone przetwarzanie - mogą Cię zainteresować. Tu masz artykuł o tym, jak autor uzyskał 60-krotne przyspieszenie łącząc oba moduły ;)
w tym microbenchmarku, który zrobiłem, okazał się wolniejszy - gdzieś w połowie drogi między pętlą for a list(map(cośtam)) -
superdurszlak2019-02-10 14:49
ano, zależy jak zrobisz, na czym odpalisz i dostaniesz inny wynik - właściwie dla każdej hipotezy możesz spreparować benchmark, który ją potwierdzi lub obali :D -
superdurszlak2019-02-10 15:14
zresztą, kichać te ułamki procentów, ktoś wjedzie z numbą i daskiem i w te 50ms zmieli 10x tyle wierszy -
superdurszlak2019-02-10 15:18
Witam,
Dziękuję za wszystkie odpowiedzi :D
Funkcja check łączy się z serwerem i sprawdza dane odesłane przez serwer. Czasami zajmuje to z 10-20 sekund także dość długo jeżeli robię to linia po linii. Dlatego chciałem użyć multithreadingu.
Witam,
Dziękuję za wszystkie odpowiedzi :D
Funkcja check łączy się z serwerem i sprawdza dane odesłane przez serwer. Czasami zajmuje to z 10-20 sekund także dość długo jeżeli robię to linia po linii. Dlatego chciałem użyć multithreadingu.
Jeśli wysyła dane na serwer, multithreading niewiele da, o ile serwer też nie jest wielowątkowy - napisałeś go sam, czy korzystasz z czegoś gotowego? Serwer dysponuje pulą wątków, kolejkuje zadania dla jakiegoś wątku przetwarzającego dane, czy spawnuje nowy wątek per połączenie? Załóżmy, że jest - problemem może się okazać brać mocy przerobowych, jeśli przetwarzania jest dużo. Jeśli jest go mało, to całość przetwarzania będzie spowalniać komunikacja. Zresztą, nawet jeśli większość czasu to jakieś obliczenia na serwerze i serwer wykorzystuje prawie całą moc, to nadal masz narzut wynikający z przesyłania komunikatów do serwera 50000 razy - lepiej byłoby wysłać plik w całości i przenieść sprawdzanie pliku linia po linii do kodu serwera.