Programmering
Deel
Het versnellen van berekeningen is een doel dat iedereen wil bereiken. Wat als u een script heeft dat tien keer sneller kan draaien dan de huidige looptijd? In dit artikel zullen we kijken naar Python-multiprocessing en een bibliotheek met de naammultiverwerking
. We zullen praten over wat multiprocessing is, de voordelen ervan, en hoe u de looptijd van uw Python-programma's kunt verbeteren door parallel programmeren te gebruiken.
Oké, dus laten we gaan!
Een inleiding tot het parallellisme
Voordat we in Python-code duiken, moeten we het eerst hebben overparallel computergebruik, wat een belangrijk concept is in de informatica.
Wanneer u een Python-script uitvoert, wordt uw code meestal op een gegeven moment een proces en wordt het proces uitgevoerd op een enkele kern van uw CPU. Maar moderne computers hebben meer dan één kern, dus wat als u meer kernen zou kunnen gebruiken voor uw berekeningen? Het blijkt dat uw berekeningen sneller zullen zijn.
Laten we dit voorlopig als algemeen principe nemen, maar later in dit artikel zullen we zien dat dit niet universeel waar is.
Zonder al te veel in details te treden: het idee achter parallellisme is om je code zo te schrijven dat deze meerdere cores van de CPU kan gebruiken.
Laten we, om het gemakkelijker te maken, eens naar een voorbeeld kijken.
Parallelle en seriële computers
Stel je voor dat je een groot probleem moet oplossen en dat je alleen bent. Je moet de vierkantswortel van acht verschillende getallen berekenen. Wat doe je? Nou, je hebt niet veel opties. Je begint met het eerste getal en berekent het resultaat. Dan ga je verder met de anderen.
Wat als je drie vrienden hebt die goed zijn in wiskunde en die je willen helpen? Elk van hen berekent de wortel van twee getallen, en uw werk zal gemakkelijker zijn omdat de werklast gelijkelijk over uw vrienden wordt verdeeld. Dit betekent dat uw probleem sneller wordt opgelost.
Oké, dus alles duidelijk? In deze voorbeelden vertegenwoordigt elke vriend een kern van de CPU. In het eerste voorbeeld wordt de hele taak opeenvolgend door u opgelost. Dit heetseriële computers. In het tweede voorbeeld gebruik je, omdat je in totaal met vier kernen werktparallel computergebruik. Parallel computing omvat het gebruik van parallelle processen of processen die over meerdere kernen in een processor zijn verdeeld.
Modellen voor parallelle programmering
We hebben vastgesteld wat parallel programmeren is, maar hoe gebruiken we het? We zeiden al eerder dat parallel computergebruik de uitvoering van meerdere taken tussen meerdere kernen van de processor inhoudt, wat betekent dat die taken tegelijkertijd worden uitgevoerd. Er zijn een paar vragen die u moet overwegen voordat u parallellisatie benadert. Zijn er bijvoorbeeld andere optimalisaties die onze berekeningen kunnen versnellen?
Laten we er voorlopig van uitgaan dat parallellisatie de beste oplossing voor u is. Het zijn er hoofdzakelijk driemodellenbij parallelle berekeningen:
- Perfect parallel. De taken kunnen onafhankelijk worden uitgevoerd en hoeven niet met elkaar te communiceren.
- Parallellisme in gedeeld geheugen. Processen (of threads) moeten communiceren, dus delen ze een globale adresruimte.
- Bericht overslaan. Processen moeten berichten delen wanneer dat nodig is.
In dit artikel illustreren we het eerste model, dat tevens het eenvoudigste is.
Python Multiprocessing: procesgebaseerd parallellisme in Python
Een manier om parallellisme in Python te bereiken is door gebruik te maken van demultiverwerkingsmodule. Demultiverwerking
Met de module kunt u meerdere processen maken, elk met zijn eigen Python-interpreter. Om deze reden bereikt Python-multiprocessing procesgebaseerd parallellisme.
Misschien heb je wel eens van andere bibliotheken gehoord, zoalsdraadsnijden
, dat ook in Python is ingebouwd, maar er zijn cruciale verschillen tussen beide. Demultiverwerking
module creëert nieuwe processen, terwijldraadsnijden
creëert nieuwe draden.
In het volgende gedeelte bekijken we de voordelen van het gebruik van multiprocessing.
Voordelen van het gebruik van multiprocessing
Hier zijn een paar voordelen van multiprocessing:
- beter gebruik van de CPU bij taken met een hoge CPU-intensiteit
- meer controle over een kind vergeleken met draden
- gemakkelijk te coderen
Het eerste voordeel heeft te maken met prestaties. Omdat multiprocessing nieuwe processen creëert, kun je de rekenkracht van je CPU veel beter benutten door je taken over de andere cores te verdelen. De meeste processors zijn tegenwoordig multi-coreprocessors, en als u uw code optimaliseert, kunt u tijd besparen door berekeningen parallel op te lossen.
Het tweede voordeel betreft een alternatief voor multiprocessing, namelijk multithreading. Threads zijn echter geen processen, en dit heeft zijn gevolgen. Als u een thread maakt, is het gevaarlijk om deze te beëindigen of zelfs te onderbreken, zoals u bij een normaal proces zou doen. Omdat de vergelijking tussen multiprocessing en multithreading niet binnen het bestek van dit artikel valt, raad ik u aan er wat verder over te lezen.
Het derde voordeel van multiprocessing is dat het vrij eenvoudig te implementeren is, aangezien de taak die u probeert uit te voeren geschikt is voor parallel programmeren.
Aan de slag met Python Multiprocessing
We zijn eindelijk klaar om wat Python-code te schrijven!
We beginnen met een heel eenvoudig voorbeeld en gebruiken het om de kernaspecten van Python-multiprocessing te illustreren. In dit voorbeeld hebben we twee processen:
- De
ouder
proces. Er is slechts één ouderproces, dat meerdere kinderen kan hebben. - De
kind
proces. Dit wordt voortgebracht door de ouder. Elk kind kan ook nieuwe kinderen krijgen.
We gaan gebruik maken van dekind
proces om een bepaalde functie uit te voeren. Op deze manier wordt deouder
kan doorgaan met de uitvoering ervan.
Een eenvoudig Python-multiprocessing-voorbeeld
Dit is de code die we voor dit voorbeeld zullen gebruiken:
vanmultiverwerkingimporterenProceszeker bubble_sort(reeks):rekening= WAAR terwijlrekening== WAAR:rekening= Vals vooriin bereik(0, len(reeks)-1): alsreeks[i] >reeks[i+1]:rekening= WAARtemperatuur=reeks[i]reeks[i] =reeks[i+1]reeks[i+1] =temperatuurafdrukken("Matrix gesorteerd:",reeks)als__naam__== '__voornaamst__':P=Proces(doel=bubble_sort,arg=([1,9,4,5,2,6,8,4],))P.begin()P.meedoen()
In dit fragment hebben we een functie gedefinieerd met de naambubble_sort(array)
. Deze functie is een heel naïeve implementatie van het Bubble Sort-sorteeralgoritme. Als u niet weet wat het is, hoeft u zich geen zorgen te maken, want het is niet zo belangrijk. Het cruciale om te weten is dat het een functie is die enig werk doet.
De Process-klasse
Vanmultiverwerking
, importeren we de klasseProces
. Deze klasse vertegenwoordigt een activiteit die in een afzonderlijk proces wordt uitgevoerd. Je kunt inderdaad zien dat we een paar argumenten hebben doorgegeven:
target=bubbelsortering
, wat betekent dat ons nieuwe proces debubble_sort
functieargs=([1,9,4,52,6,8,4],)
, wat de array is die als argument aan de doelfunctie wordt doorgegeven
Nadat we een instantie voor de klasse Process hebben gemaakt, hoeven we alleen maar het proces te starten. Dit gebeurt schriftelijkp.start()
. Op dit punt wordt het proces gestart.
Voordat we afsluiten, moeten we wachten tot het onderliggende proces zijn berekeningen heeft voltooid. Demeedoen()
methode wacht tot het proces is beëindigd.
In dit voorbeeld hebben we slechts één onderliggend proces gemaakt. Zoals je misschien wel raadt, kunnen we meer onderliggende processen creëren door meer instances te maken in deProces
klas.
De Poolklasse
Wat als we meerdere processen moeten creëren om meer CPU-intensieve taken uit te voeren? Moeten we altijd beginnen en expliciet wachten op beëindiging? De oplossing hier is om deZwembad
klas.
DeZwembad
Met class kunt u een pool van werkprocessen maken, en in het volgende voorbeeld zullen we bekijken hoe we deze kunnen gebruiken. Dit is ons nieuwe voorbeeld:
vanmultiverwerkingimporterenZwembadimporterentijdimporterenwiskundeN= 5000000zeker kubus(X): opbrengstwiskunde.sqrt(X)als__naam__== "__voornaamst__": metZwembad() alszwembad:resultaat=zwembad.kaart(kubus, bereik(10,N)) afdrukken("Programma voltooid!")
In dit codefragment hebben we eenkubus(x)
functie die eenvoudigweg een geheel getal neemt en de vierkantswortel retourneert. Makkelijk, toch?
Vervolgens maken we een exemplaar van deZwembad
klasse, zonder enig attribuut op te geven. De poolklasse maakt standaard één proces per CPU-kern. Vervolgens voeren we dekaart
methode met een paar argumenten.
Dekaart
methode past dekubus
functie voor elk element van de iterabele die we leveren - wat in dit geval een lijst is van elk getal uit10
naarN
.
Het grote voordeel hiervan is dat de berekeningen op de lijst parallel worden uitgevoerd!
Optimaal gebruik maken van Python Multiprocessing
Het creëren van meerdere processen en het uitvoeren van parallelle berekeningen is niet noodzakelijkerwijs efficiënter dan serieel computergebruik. Voor taken die weinig CPU-intensief zijn, zijn seriële berekeningen sneller dan parallelle berekeningen. Om deze reden is het belangrijk om te begrijpen wanneer u multiprocessing moet gebruiken, wat afhangt van de taken die u uitvoert.
Om u hiervan te overtuigen, laten we een eenvoudig voorbeeld bekijken:
vanmultiverwerkingimporterenZwembadimporterentijdimporterenwiskundeN= 5000000zeker kubus(X): opbrengstwiskunde.sqrt(X)als__naam__== "__voornaamst__": # eerste manier, met behulp van multiprocessingstarttijd=tijd.perf_teller() metZwembad() alszwembad:resultaat=zwembad.kaart(kubus, bereik(10,N))eindtijd=tijd.perf_teller() afdrukken("Programma voltooid in {} seconden - met behulp van multiprocessing".formaat(eindtijd-starttijd)) afdrukken("---") # tweede manier, seriële berekeningstarttijd=tijd.perf_teller()resultaat= [] voorXin bereik(10,N):resultaat.toevoegen(kubus(X))eindtijd=tijd.perf_teller() afdrukken("Programma voltooid in {} seconden".formaat(eindtijd-starttijd))
Dit fragment is gebaseerd op het vorige voorbeeld. We lossen hetzelfde probleem op, namelijk het berekenen van de vierkantswortel vanN
cijfers, maar op twee manieren. De eerste omvat het gebruik van Python-multiprocessing, terwijl de tweede dat niet doet. We gebruiken deperf_counter()
methode uit detijd
bibliotheek om de tijdprestaties te meten.
Op mijn laptop krijg ik dit resultaat:
>python code.pyProgram voltooidin 1,6385094seconden - met behulp van multiprocessing --- Programma voltooidin 2,7373942999999996seconden
Zoals u kunt zien, is er meer dan een seconde verschil. In dit geval is multiprocessing dus beter.
Laten we iets in de code veranderen, zoals de waarde vanN
. Laten we het verlagen naarN=10000
en kijk wat er gebeurt.
Dit is wat ik nu krijg:
>python code.pyProgram voltooidin 0,3756742seconden - met behulp van multiprocessing --- Programma voltooidin 0,005098400000000003seconden
Wat is er gebeurd? Het lijkt erop dat multiprocessing nu een slechte keuze is. Waarom?
De overhead die wordt veroorzaakt door het opsplitsen van de berekeningen over de processen is te veel vergeleken met de opgeloste taak. Je ziet hoeveel verschil er is qua tijdprestaties.
Conclusie
In dit artikel hebben we het gehad over de prestatie-optimalisatie van Python-code door gebruik te maken van Python-multiprocessing.
Eerst hebben we kort geïntroduceerd wat parallel computing is en de belangrijkste modellen om het te gebruiken. Toen begonnen we te praten over multiprocessing en de voordelen ervan. Uiteindelijk hebben we gezien dat het parallelliseren van de berekeningen niet altijd de beste keuze ismultiverwerking
module moet worden gebruikt voor het parallelliseren van CPU-gebonden taken. Zoals altijd is het een kwestie van nadenken over het specifieke probleem waarmee u wordt geconfronteerd en het evalueren van de voor- en nadelen van de verschillende oplossingen.
Ik hoop dat je het leren over Python-multiprocessing net zo nuttig hebt gevonden als ik.