Како побољшати свој Питхон код уз истовременост и паралелизам

Ključne Informacije

  • Konkurentnost i paralelizam su temeljna načela izvršavanja zadataka u računarstvu, svako sa svojim jedinstvenim karakteristikama.
  • Konkurentnost omogućava efikasno iskorišćavanje resursa i poboljšava odziv aplikacija, dok je paralelizam presudan za postizanje optimalnih performansi i skalabilnosti.
  • Python nudi različite pristupe upravljanju konkurentnošću, poput niti i asinhronog programiranja sa bibliotekom `asyncio`, kao i paralelizam putem modula `multiprocessing`.

Konkurentnost i paralelizam su dva pristupa koji omogućavaju istovremeno pokretanje više programa. Python nudi više opcija za rukovanje zadacima istovremeno i paralelno, što ponekad može biti zbunjujuće.

Istražimo alate i biblioteke dostupne za ispravnu implementaciju konkurentnosti i paralelizma u Pythonu, te po čemu se oni međusobno razlikuju.

Razumevanje Konkurentnosti i Paralelizma

Konkurentnost i paralelizam su dva osnovna principa izvršavanja zadataka u računarstvu. Svaki od njih poseduje svoje specifične karakteristike.

  • Konkurentnost se odnosi na sposobnost programa da upravlja većim brojem zadataka u isto vreme, bez nužnog izvršavanja svih njih istovremeno. Suština je u preplitanju zadataka, prebacivanju između njih tako da deluje kao da se odvijaju simultano.
  • Paralelizam, s druge strane, podrazumeva istovremeno izvršavanje više zadataka, koristeći prednosti višejedarnih CPU-a ili više procesora. Paralelizam omogućava pravo simultano izvršavanje, što omogućava brže obavljanje zadataka, posebno kada su u pitanju operacije koje zahtevaju mnogo računanja.

Važnost Konkurentnosti i Paralelizma

Potreba za konkurentnošću i paralelizmom u računarstvu ne može se zanemariti. Evo zašto su ove tehnike bitne:

  • Iskorišćavanje resursa: Konkurentnost omogućava efikasno korišćenje sistemskih resursa, osiguravajući da zadaci aktivno napreduju umesto da besposleno čekaju na spoljne resurse.
  • Odziv: Konkurentnost može poboljšati odziv aplikacija, naročito u scenarijima koji uključuju korisničke interfejse ili web servere.
  • Performanse: Paralelizam je ključan za postizanje optimalnih performansi, posebno kod zadataka koji su CPU-intenzivni, kao što su složeni proračuni, obrada podataka i simulacije.
  • Skalabilnost: I konkurentnost i paralelizam su od suštinske važnosti za izgradnju skalabilnih sistema.
  • Prilagodljivost budućnosti: Kako hardverski trendovi nastavljaju favorizovati procesore sa više jezgara, sposobnost iskorišćavanja paralelizma postaje sve bitnija.

Konkurentnost u Pythonu

Konkurentnost u Pythonu možete postići korišćenjem niti (threading) i asinhronog programiranja sa `asyncio` bibliotekom.

Niti u Pythonu

Threading je mehanizam konkurentnosti u Pythonu koji vam omogućava kreiranje i upravljanje zadacima unutar jednog procesa. Niti su pogodne za određene tipove zadataka, posebno one koji su vezani za ulaz/izlaz (I/O) i mogu imati koristi od istovremenog izvršavanja.

Pythonov modul za rad sa nitima pruža jednostavan interfejs za kreiranje i upravljanje nitima. Iako GIL (Global Interpreter Lock) ograničava niti u smislu pravog paralelizma, one i dalje mogu postići konkurentnost efikasnim preplitanjem zadataka.

Kod u nastavku pokazuje primer implementacije konkurentnosti koristeći niti. Koristi Python biblioteku `requests` za slanje HTTP zahteva, što je uobičajen zadatak koji blokira I/O. Takođe koristi modul `time` za merenje vremena izvršavanja.

 import requests
 import time
 import threading

 urls = [
     'https://www.google.com',
     'https://www.wikipedia.org',
     'https://www.makeuseof.com',
 ]

 def download_url(url):
     response = requests.get(url)
     print(f"Preuzeto {url} - Status Kod: {response.status_code}")

 start_time = time.time()

 for url in urls:
     download_url(url)

 end_time = time.time()
 print(f"Sekvencijalno preuzimanje trajalo {end_time - start_time:.2f} sekundi\n")

 start_time = time.time()
 threads = []

 for url in urls:
     thread = threading.Thread(target=download_url, args=(url,))
     thread.start()
     threads.append(thread)

 for thread in threads:
     thread.join()

 end_time = time.time()
 print(f"Preuzimanje sa nitima trajalo {end_time - start_time:.2f} sekundi")

 

Pokretanjem ovog programa, trebalo bi da vidite koliko su zahtevi sa nitima brži od sekvencijalnih zahteva. Iako je razlika samo delić sekunde, dobijate jasan utisak o poboljšanju performansi kada koristite niti za I/O vezane zadatke.

Asinhrono Programiranje sa Asyncio

`asyncio` obezbeđuje petlju događaja koja upravlja asinhronim zadacima koji se nazivaju korutine. Korutine su funkcije koje se mogu pauzirati i nastaviti, što ih čini idealnim za zadatke vezane za I/O. Biblioteka je posebno korisna za scenarije u kojima zadaci uključuju čekanje na spoljne resurse, kao što su mrežni zahtevi.

Možete modifikovati prethodni primer slanja zahteva da radi sa `asyncio`:

 import asyncio
 import aiohttp
 import time

 urls = [
     'https://www.google.com',
     'https://www.wikipedia.org',
     'https://www.makeuseof.com',
 ]

 async def download_url(url):
     async with aiohttp.ClientSession() as session:
         async with session.get(url) as response:
             content = await response.text()
             print(f"Preuzeto {url} - Status Kod: {response.status}")

 async def main():
     tasks = [download_url(url) for url in urls]
     await asyncio.gather(*tasks)

 start_time = time.time()
 asyncio.run(main())
 end_time = time.time()
 print(f"Asyncio preuzimanje trajalo {end_time - start_time:.2f} sekundi")

 

Korišćenjem koda, možete istovremeno preuzimati veb stranice koristeći `asyncio` i iskoristiti prednosti asinhronih I/O operacija. Ovo može biti efikasnije od niti za I/O zadatke.

Paralelizam u Pythonu

Paralelizam možete implementirati koristeći Pythonov modul za `multiprocessing`, što vam omogućava da u potpunosti iskoristite prednosti višejezgrenih procesora.

Multiprocesiranje u Pythonu

Pythonov modul za više procesa pruža način za postizanje paralelizma kreiranjem odvojenih procesa, svaki sa svojim Python interpreterom i memorijskim prostorom. Ovo efektivno zaobilazi Globalno zaključavanje interpretera (GIL), što ga čini pogodnim za zadatke vezane za CPU.

 import requests
 import multiprocessing
 import time

 urls = [
     'https://www.google.com',
     'https://www.wikipedia.org',
     'https://www.makeuseof.com',
 ]

 def download_url(url):
     response = requests.get(url)
     print(f"Preuzeto {url} - Status Kod: {response.status_code}")

 def main():
     num_processes = len(urls)
     pool = multiprocessing.Pool(processes=num_processes)

     start_time = time.time()
     pool.map(download_url, urls)
     end_time = time.time()
     pool.close()
     pool.join()

     print(f"Multiprocesno preuzimanje trajalo {end_time-start_time:.2f} sekundi")

 main()

 

U ovom primeru, multiprocesiranje pokreće više procesa, omogućavajući funkciji `download_url` da radi paralelno.

Kada Koristiti Konkurentnost ili Paralelizam

Izbor između konkurentnosti i paralelizma zavisi od prirode vaših zadataka i dostupnih hardverskih resursa.

Možete koristiti konkurentnost kada se bavite zadacima koji su vezani za I/O, kao što su čitanje i pisanje u datoteke ili slanje mrežnih zahteva, i kada su ograničenja memorije bitna.

Koristite multiprocesiranje kada imate zadatke koji su vezani za CPU i mogu imati koristi od pravog paralelizma, i kada imate jasnu izolaciju između zadataka, gde neuspeh jednog zadatka ne bi trebalo da utiče na druge.

Iskoristite Prednosti Konkurentnosti i Paralelizma

Paralelizam i konkurentnost su efikasni načini za poboljšanje odziva i performansi vašeg Python koda. Važno je razumeti razlike između ovih koncepata i izabrati najefikasniju strategiju.

Python nudi alate i module koji su vam potrebni da učinite vaš kod efikasnijim kroz konkurentnost ili paralelizam, bez obzira da li radite sa procesima koji su vezani za CPU ili za I/O.