Bakgrund

Idag tänkt jag skriva några rader om att skriva concurrent kod, eller “samtidighet” om vi prompt ska översätta det till svenska. För att enklare förstå vad vi gör behöver vi titta på vilken typ av problem vi försöker lösa, först ska vi bara reda ut ett par begrepp.

I vår värld som kretsar mycket kring automation av nätverk och annan IT infrastruktur så kommer majoriteten av programmens körtid inte handla om beräkningar som vår CPU behöver göra, utan snarare väntetider på externa faktorer så som att t.ex kommunicera med enheter över nätverket. När det är så kallad I/O som är programmets flaskhals brukar man säga att mjukvaran är “I/O bound”. Till skillnad program som körs helt lokalt och bara väntar på att CPU:n ska göra ett antal beräkningar där processorn är flaskhalsen, dessa program kallas istället för CPU bound.

Idag tänkte jag att vi ska kika på hur man med enkla medel accelererar I/O bound kod igenom att göra den concurrent!

Vad är concurrency

Vad innebär då concurrency? Concurrency innebär att vi låter programmet starta flera uppgifter utan att ständigt blockera och vänta på att föregående uppgift är helt slutförd. I exemplet vi ska titta på så kommer vi skriva ett enkelt script som pingar tre olika domäner. Majoriteten av tiden här kommer bestå av väntetid medan vår ping traverserar internet till destinationen och till dess att svaret når oss igen. Det är knappast CPU intensivt och därför är det synd att blockera hela programmet och vänta. Istället skickar vi nästa ping direkt efter att den första är skickad, alltså innan vi fått vårt första svar har vi skickat iväg alla tre pingar så att vi kan vänta på alla svar samtidigt istället för att vänta på svar innan vi pingar nästa.

Concurrency är dock inte samma sak som parallelism. Här pratar vi alltså inte om att göra saker samtidigt i olika processorer utan att schemalägga delar av uppgifter så att vi kan nyttja väntetid i en uppgift till att göra nödvändiga beräkningar i en annan och på så viss multitaska effektivt mellan uppgifter eftersom våra uppgifter ofta består till stor del av väntetid!

image

Exempel

image

För att visa vad som händer utan concurrency använder vi följande exempelkod:

image

När vi kör koden ser vi att den totala tiden är lite drygt summan av round trip tiden för varje ping.

Vi väntar alltså på föregående svar innan vi skickar nästa ping.

Concurrent Python

Sedan python version 3.5 har vi kunnat använda syntaxen async/await vilket ger oss bra möjligheter att göra det här på ett lätthanterligt och tydligt sätt. Ihop med asyncio som är ett defaultbibliotek i Python nu för tiden kan vi enkelt skapa coroutines som vi kör concurrent.

Så för att skapa en coroutine behöver vi skapa en funktion eller metod som är asynkron, dvs är definierad med async def. Därefter behöver vi tala om för interpretern att vi vill skapa tasks, alltså en körning av en asynkron funktion. Och slutligen vill vi samla ihop alla resultat från eventloopen.

Detta kan vi göra ganska enkelt så här:

image

När vi kör den koden istället så ser vi två intressanta saker. För det första är det alltid den domän med kortast svarstid som dyker upp först i stdout. För det andra är totaltiden avsevärt kortare. Här är den totala körtiden mycket mindre än summan av round-trip tiden vilket visar på att vi lagt väntetiderna mer eller mindre samtidigt!

image

Sammanfattning

I takt med att vi gör mer saker i våra program byggs alltså den totala väntetiden upp allt mer. Låt säga att vi har ett program som loggar in i en router via SSH för att uppdatera en ACL som används för VTY access så är det troligen gjort på några få sekunder och är det en eller ett fåtal routrar blir det sällan ett problem. Men lek med tanken att vi har tusentals routrar i ett operatörsnät, då börjar tiden faktiskt bli en faktor.

En annan faktor som blir mer och mer aktuell är också när vi skriver kod som körs serverless där man faktiskt betalar mer pengar ju längre tid vi kör koden. I AWS Lambda t.ex. betalar man pengar per 100 millisekunder som koden exekveras. Kan vi halvera tiden halveras också kostnaderna. När man tittar på den typen av usecase blir det ännu mer tydligt att långsam och/eller ineffektiv kod kostar mer pengar att köra och det gäller egentligen också on-premise.

Med några enkla knep kan vi reducera exekveringstiden avsevärt när vi arbetar med I/O bound kod. I många fall kan det kännas overkill i början, men när projekten växer märker vi snabbt nyttan med att skriva skalbar kod från början.


Senior Consultant


2025

Acebit expanderar

1 minute read

Acebit expanderar – automationsess från Dalarna öppnar i Stockholm

Back to Top ↑

2024

Acebit i Falu-Kuriren

less than 1 minute read

Även Falu-Kuriren har intresserat sig för Acebit och besökt oss i Falun!

Back to Top ↑

2023

Meraki API & Mgmt interface

4 minute read

Nyligen stötte jag på ett scenario som innebar att ca 200 Meraki-enheter behövde byta inställning från DHCP till statisk IP-adressering. Istället för att gör...

Arbeta med VPC Del3 - Konfiguration

3 minute read

I mina tidigare inlägg om virtuella port-channels kikade vi på vad det är och vilka delar de utgör. Det här avsnittet kommer beröra hur en grundkonfiguration...

Få upp farten med concurrency

3 minute read

Bakgrund Idag tänkt jag skriva några rader om att skriva concurrent kod, eller “samtidighet” om vi prompt ska översätta det till svenska. För att enklare för...

Back to Top ↑

2022

Back to Top ↑

2021

Back to Top ↑