Anslut till Senaste Tekniska Nyheter, Bloggar, Recensioner

Att förstå Intel Haswell Transactional Synchronization Extensions


Flerkärnig och låsning

Intel har släppt ytterligare information på deras Transactional Synchronization-teknologi (TSX), som i grunden är en instruktionsuppsättningsarkitektur (ISA)-tillägg för att göra hårdvaruaccelererat transaktionsminne möjligt. Vad betyder det i den verkliga världen?

Ju fler kärnor du får i ett system, desto fler trådar behöver du för att hålla dem sysselsatta. Tyvärr är det inte så lätt att göra det: trådar måste fungera på delad data och behöver lås för att säkerställa att slutresultatet blir korrekt. En tråd måste få ett lås, vilket kan kräva att man väntar tills en annan tråd släpper låset. Det kan leda till allvarliga låskonflikter som kan resultera i dålig skalning, till och med till den punkt där fler kärnor (och trådar) kan leda till en prestandaförlust istället för en vinst.

Vi mätte detta i ett verkligt riktmärke (baserat på MySQL InnoDB) och visade att spinnande lås verkligen var skyldiga. Spinlocks som är “upptagna med att vänta” för mycket gör mer än att bara förstöra skalbarheten hos din flerkärniga server: de kan slösa bort en hel del energi. Eftersom kärnor knappt utför något användbart arbete och inte längre kan gå i vila, kan en ström av spinlocks orsaka förödelse på en servers prestanda per watt-förhållande. Följande diagram, baserat på mätningar på ett HPC-riktmärke, är ett annat exempel. Att titta på prestandan som du får från varje kärna hjälper till att sammanfatta problemet.

HPC-bänken visar hur ju fler kärnor programvaran använder, desto mindre prestanda får vi per kärna. När 16 kärnor fungerar, erbjuder varje kärna endast 60 % av prestandan för en enda gängad körning. En delad datastruktur är problemet. Noggrann justering kan göra att denna mjukvara skalas bättre, men detta skulle kräva en betydande mängd tid och ansträngning.

Roten till låsproblemen är att låsning är en avvägning. Anta att du har en delad datastruktur som en liten tabell eller lista. Om utvecklaren är tidsbegränsad, vilket ofta är fallet, är det enklaste sättet att garantera konsistens att låta en viss tråd låsa hela datastrukturen (t.ex. tabelllåset för MySQL MyISAM). Tråden skaffar låset och exekverar sedan dess kod (den kritiska delen). Under tiden är alla andra gängor blockerade tills gängan släpper låset på strukturen. Det är dock bara nödvändigt att låsa en bit data om två trådar vill skriva till den. Det klassiska exemplet är en sekvens av två banktransaktioner:


Att låsa en hel datastruktur är något annat. Du kan tänka dig att de andra trådarna alla kanske skriver till olika värden och borde kunna göra mycket arbete parallellt. Den grovkorniga låsningen av hela strukturer är i allmänhet inte nödvändig, men det underlättar utvecklarnas arbete.

Utvecklaren skulle kunna skriva om sin kod och tillämpa en mycket finare låsning, men detta är en ganska komplex och tidskrävande uppgift, och den kritiska koddelen kommer förmodligen att behöva flera variabler för att göra sitt jobb. I så fall kan du få dödläge: en tråd låser variabel A, en annan trådvariabel B. De behöver båda A och B, och de fortsätter att försöka få ett lås på den andra variabeln. Det finns en anledning till att det kräver en stor investering av tid och pengar för att få bra skalbarhet med flera kärnor.