Kerry Johnson har 20 års erfarenhet från industrin, primärt inom telekommunikation. Innan han började på QNX arbetade han bland annat på Nortel, CrossKeys, and Research in Motion, Den här e-postadressen skyddas mot spambots. Du måste tillåta JavaScript för att se den. |
Varje Posixprocess består i sin tur av ett eller flera exekveringsflöden, så kallade trådar. De schemaläggs för exekvering helt oberoende av varandra, även om de tillhör samma process. Däremot delar alla trådar i samma process gemensamma resurser som filpekare och minne. Det betyder att en tråd kan påverka andra trådar, exempelvis när den stänger en fil eller uppdaterar en global variabel. Programutvecklaren måste därför synkronisera sina trådar för att säkerställa dataintegritet.
Posix 1003.1c tillhandahåller ett programmeringsgränssnitt för trådar som kallas ptrådar (pthreads), funktioner som hanterar trådgenerering, -terminering, synkronisering med mera. Posix 1003.1c definierar också en modell för prioritetsbaserad schemaläggning av trådar. Realtidsoperativsystem (RTOS) som QNX Neutrino använder avbrytande (preemptive), prioritetsbaserad schemaläggning vilket innebär att den högst prioriterade tråden väljs ut för exekvering så snart den är redo.
Ett symmetriskt multiprocessorsystem består av flera identiska CPU-kärnor som delar på minnesutrymme och periferienheter via en gemensam buss för data och styrsignaler. (Se figur 2). Kärnorna kan vara diskreta, singelkärnor eller multikärnor. Termen symmetrisk betyder att varje CPU-kärna har samma åtkomstmekanism för det gemensamt minne och periferienher.
FOTNOT: Artikeln innehåller ytterligare ett antal bilder som inte publiceras här på webben. I magasinet Elektroniktidningen nummer 4 2007 finns den kompletta artikeln. En prenumeration kostar endast noll kronor. |
Under SMP styr en ensam kopia av operativsystemet samtliga CPU-enheter parallellt. Eftersom CPU-enheternas har gemensam åtkomst till minnesutrymmet kan kommunikation och synkronisering mellan alla CPU-enheter ske med ett enkelt Posixkommando, som en semafor, eller egen lokal transport. I båda fallen ger detta mycket högre prestanda än att gå via nätverksprotokoll.
Multikärnesystem är teoretiskt mycket snabbare än singelkärnor. Genom att använda sig av arbetstrådar och annan teknik kan man dra maximal nytta av detta.
Ett SMP-kapabelt operativsystem tar hand om den komplexa uppgiften att allokera och dela resurser mellan CPU-kärnorna. Antalet CPU-enheter är transparent för applikationsprogrammeraren. Resultatet blir att existerande applikationsprogram kan köras på SMP-systemet utan kodmodifieringar.
SMP-operativsystemet sprider dynamiskt ut trådar på CPU-kärnorna. Och ser till att det alltid är trådar med den högsta prioriteten som exekveras. Om högprioriterad tråd vill exekvera får den omedelbart avbryta en lågprioriterad tråd.
En icke-trådbaserad programvara kommer visserligen ändå att få förbättrad kapacitet i ett SMP-system eftersom systemet kör flera programprocesser parallellt. Men för att ytterligare höja prestanda måste programmeraren dela upp sitt eget program i parallella trådar. För utvecklare finns det då flera designmönster att välja mellan:
- Worker threads/arbetstrådar - en huvudtråd genererar arbetstrådar för olika parallella uppgifter.
- Peer - en tråd genererar trådar som tillsammans med den samarbetar omuppgiften.
- Pipelining - programmeraren bryter ned en process i mindre steg där varje processteg hanteras av olika trådar.
Anta det enkeltrådiga exemplet i figur 4, där funktionen fill_array() uppdaterar en stor tvådimensionell matris. Eftersom funktionen uppdaterar varje bildelement var för sig så är det enkelt att parallellisera processen.
För att öka hastigheten på fill_array () generar vi en arbetstråd för varje CPU som uppdaterar en del av bildmatrisen. För att skapa arbetstrådarna använder vi funktionen pthread_create(). Man specificerar startpunkten för en tråd (där tråden börjar exekvera) via en funktionspekare till pthread_create(). I exemplet startar varje arbetstråd med funktionen fill_array_fragment().
Varje arbetstråd bestämmer vilken del av matrisen den ska uppdatera. Alla arbetstrådar kan sedan bearbetas parallellt - på olika kärnor.
Eftersom huvudtråden måste invänta att hela matrisen är uppdaterad innan den kan fortsätta, behövs någon form av synkronisering. Med ptrådarna kommeren mängd synkroniseringsprimitiv, inklusive mutexar, semaforer och join. Exemplet använder ”barriärsynkronisering” - genom att anropa funktionen pthread_barrier_wait() inväntar färdiga arbetstrådar varandra.
Nu när vi väl har omvandlat programvaran till en flertrådig programmodell, kan vi få den att skala med antalet CPU-kärnor. För optimal prestanda skall antalet trådar vara detsamma som antalet CPU-kärnor. Exemplet i fig 7 använder fyra kärnor och vi kan enkelt justera antalet arbetstrådar till fler eller färre.
Även om traditionella avlusningsare på processnivå också kan hjälpa att diagnostisera vissa problem i ett flerkärnigt SMP-system så kan de inte visualisera den komplexitet på systemnivå som uppstår när man använder flertrådiga processer på multikärnor.
För att kunna förstå detta beteende i processerna och för att kunna förenkla optimeringen av flertrådiga, multi-CPU-system så behöver programutvecklare systemanalysverktyg som till exempel QNX Momentics System Profiler. Programmet fungerar tillsammans med en specialversion av QNX Neutrino RTOS som heter Instrumented Kernel. Den loggar alla systemkommandon och tillstånd så den vet exakt när trådar startar, hur länge de exekverar och när de är klara. Tidsmärkningningen är högupplöst.
Figur 8 visar en sökning i System Profiler för vårt exempel. Flertrådsapplikationen ser ut att fungerarsom förväntat. Neutrino har schemalagt trådar på varje tillgänglig CPU-kärna (visat med färgkodning). Man kan också se hur arbetstrådarna väntar på barriären tills alla är klara och sedan låter huvudtråden återuppta sin exekvering.
Vi kan också mäta prestandaförbättringen. Först mäter vi tiden för en enkeltrådsprocess som anropar fill_array() och sedan fortsätter med annat arbete - se figur 9. System profiler indikerar då att den totala exekveringstiden är 5,49 sekunder.
Figur 10 visar prestandaförbättringen med fyra arbetstrådar. Trots att arbetstrådarna exekveras på fyra kärnor är förbättringen bara 2,7 gånger. Ytterligare analys med System Profiler visar att det är den efterföljande bearbetningen i huvudtråden som är skurken i dramat - den tar 0,88 sekunder. Den är enkeltrådig och kan den inte utnyttja multikärnan.
Vilket återigen bevisar Amdahls lag, som säger att prestandaförbättringen genom parallellisering begränsas av mängden icke parallellt exekverad kod. Om man bara tittar på den parallella delen av programmet ökar prestandaförbättringen till 3,95.
Andra sätt att förbättra SMP-prestdana i ett program är att minska användningen av delade resurser och optimera cacheanvändningen. För att lyckas kräver dessa metoder analysverktyg som kan visualisera komplexa samspel och beteenden i multikärnor.