Jak zmienic ten kod na szybszy?

Odpowiedz Nowy wątek
2019-02-10 13:16
0

Witam,
Mam tutaj proste wczytywanie pliku:

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.

Pozostało 580 znaków

2019-02-10 13:39
0

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ć.

Pozostało 580 znaków

2019-02-10 13:57
1

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

Prosząc o pomoc w wiadomości prywatnej odbierasz sobie szansę na otrzymanie pomocy od kogoś bardziej kompetentnego :)
edytowany 1x, ostatnio: superdurszlak, 2019-02-10 14:39

Pozostało 580 znaków

2019-02-10 14:15
3

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 ;)

Prosząc o pomoc w wiadomości prywatnej odbierasz sobie szansę na otrzymanie pomocy od kogoś bardziej kompetentnego :)
edytowany 4x, ostatnio: superdurszlak, 2019-02-12 07:28

Pozostało 580 znaków

2019-02-10 14:24
1
  1. Wczytaj cały plik na raz za pomocą read a potem potnij na linie
  2. Pokaż co robi check
  3. Użyj PyPy

Na PW przyjmuje tylko (ciekawe!) zlecenia. Masz problem? Pisz na forum, nie do mnie.
edytowany 1x, ostatnio: Shalom, 2019-02-10 14:24
Co do PyPy - pytanie jakich modułów (i czy jakichkolwiek) używa i czy któryś nie wybuchnie w kontakcie z PyPy :) - superdurszlak 2019-02-10 14:33

Pozostało 580 znaków

2019-02-10 14:38
1
superdurszlak napisał(a):

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:
[...]

W Python 3, as_map zwraca generator. W Python 2, zgaduję, nie ma tak sensacyjnej różnicy.

Pozostało 580 znaków

2019-02-10 14:42
1

Spróbuj użyć pandas, jest najszybsze w Pythonie.


edytowany 1x, ostatnio: lion137, 2019-02-10 14:42

Pozostało 580 znaków

2019-02-10 14:45
0
Mózg napisał(a):

W Python 3, as_map zwraca generator. W Python 2, zgaduję, nie ma tak sensacyjnej różnicy.

Racja, moje niedopatrzenie. Niemniej po zmianie:

>>> def as_map(procedure):
...   return list(map(procedure, collection))

czas wykonania i tak jest ~10% krótszy, niż dla iteracji.

Edit jeszcze tak z ciekawości zastąpiłem map przez filter. Okazał się jeszcze minimalnie (może 2%) szybszy.


Prosząc o pomoc w wiadomości prywatnej odbierasz sobie szansę na otrzymanie pomocy od kogoś bardziej kompetentnego :)
edytowany 1x, ostatnio: superdurszlak, 2019-02-10 15:11
A jeszcze trochę szybsze powinno być list comprehension. - lion137 2019-02-10 14:48
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)) - superdurszlak 2019-02-10 14:49
JA widziałem inne:) Tak to widać z benchmarkami bywa. - lion137 2019-02-10 15:12
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 - superdurszlak 2019-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 - superdurszlak 2019-02-10 15:18

Pozostało 580 znaków

2019-02-10 15:45
0

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.

Pozostało 580 znaków

2019-02-10 15:53
0

A właściciel serwera się nie zdenerwuje jak będziesz mu napieprzał tysiącami żądań? ;]

Spróbuj użyć klienta asynchronicznego klienta HTTP. Google: "python async http client"


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit, 2019-02-10 15:53

Pozostało 580 znaków

2019-02-10 15:53
0
Adrian Rudy Dacka napisał(a):

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.


Prosząc o pomoc w wiadomości prywatnej odbierasz sobie szansę na otrzymanie pomocy od kogoś bardziej kompetentnego :)

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0