Кохерентност кеш меморије у вишејезгарним процесорима: како се одржава и ко је контролише

Последње ажурирање: КСНУМКС март КСНУМКС
  • Кохерентност кеша осигурава да све копије истих података у различитим кеш меморијама и РАМ меморији остану конзистентне на вишејезгарним системима.
  • Хијерархија кеша са дељеним последњим нивоом поједностављује контролу конзистентности и смањује директне приступе главној меморији.
  • Протоколи кохерентности користе стратегије поништавања или ажурирања копија, подржане стањима и контролним битовима по линији кеша.
  • Компилатор и оперативни систем могу допунити конзистентност хардвера уметањем инструкција и конфигурисањем меморије за критичне периоде.

Шема кохерентности кеш меморије процесора

Када погледате дијаграм било ког модерног вишејезгарног процесора, увек се појављује исти образац: више језгара, свако са својим оближњим кешом, и заједнички кеш последњег нивоа који делује као заједничка тачка пре него што стигне до РАМ меморије. Овај распоред није случајан или хир дизајнера, већ директан одговор на критични проблем у паралелним системима: кохерентност кеша.

Без робусног механизма конзистентности, свако језгро би могло да заврши са радом са другачијом и застарелом верзијом истих података у меморији , што се у стварном програму преводи у суптилне грешке, непредвидиве кварове, па чак и падове система. Стога је разумевање како се ова конзистентност одржава – и на нивоу хардвера и софтвера – кључно за разумевање перформанси и стабилности модерних вишејезгарних процесора.

Шта је кохерентност кеша: метафора терминала

реално време у електронским системима
Повезани чланак:
Електронски системи у реалном времену: основе, планирање и примене

Замислите неколико људи који седе испред различитих терминала, сви уређују исти документ сачуван на централном серверу . Сваки екран приказује копију датотеке, а очекује се да ће се све измене које једна особа направи одмах одразити на екранима свих осталих.

Да би ово функционисало, потребан је механизам синхронизације који преноси измене документа на све терминале, тако да сви увек виде исту верзију. Докле год овај систем ради, све је у реду: ко год измени текст зна да ће сви остали видети нову верзију готово тренутно.

Сада замислите да систем синхронизације изненада откаже. Свака особа наставља са уређивањем, уверена да ради на дељеном документу, али у стварности, сваки терминал остаје са својом неповезаном локалном копијом . Од тог тренутка, измене које направи једна особа не стижу до осталих, и документ почиње неконтролисано да се раздваја.

У области рачунарства, управо би се то десило ако процесору недостаје поуздан протокол за конзистентност: једно језгро модификује податке у меморији, али остала језгра настављају да читају старију верзију из својих приватних кеш меморија . Ово ствара плодно тло за озбиљне логичке грешке, оштећене податке и понашање приликом откривања грешака.

Кохерентност кеша је стога скуп механизама који осигуравају да, у вишејезгарном систему, све копије истих података распоређених по различитим кеш меморијама и RAM меморији одржавају конзистентно стање . Чак и ако постоји више копија, систем се мора понашати „као да“ постоји само једна.

Хијерархија кеш меморије у вишејезгарним процесорима

Кеш меморија и хијерархија меморије у вишејезгарном процесору

Кеш меморије процесора су мале, веома брзе меморије које чувају копије често коришћених делова РАМ меморије . Када процесор извршава код, уместо да континуирано приступа (релативно спорој) РАМ меморији, он покушава да чита из кеша и пише у њега, драстично смањујући латенцију.

Цака је, наравно, у томе што кеш меморије не чувају „званичну верзију“ података, већ само привремену копију . Пратећи метафору терминала, RAM меморија би била документ на серверу, док би кеш меморија била локални екрани који приказују копије одређених делова датотеке.

Код вишејезгарног процесора, дизајн постаје сложенији јер свако језгро обично има своје приватне кеш меморије нивоа 1 (L1) и чак нивоа 2 (L2) . Изнад њих се додаје заједничка кеш меморија нивоа 3 (на пример), која се налази између језгара и контролера меморије који омогућава приступ RAM меморији.

Овај дељени кеш је уведен зато што би омогућавање свим језгрима да директно и интензивно приступају RAM меморији изазвало сукобе приступа, конкуренцију на меморијској магистрали и значајан пад перформанси . Кеш последњег нивоа делује као заједнички „бафер“ који смањује приступ RAM меморији и централизује велики део саобраћаја података.

  Комплетан водич за надоградњу компоненти рачунара и побољшање перформанси

Штавише, многе архитектуре организују кеш меморије инклузивно: линије смештене на нивоима близу процесора присутне су и на вишим нивоима хијерархије . То јест, линија која се појављује у L1 је такође у L2 и, заузврат, у L3. Ово има веома корисну последицу за конзистентност: једноставно ажурирање кеша најнижег нивоа је довољно да контролише стање осталих нивоа без потребе за сталним приступом RAM меморији.

Зашто је дељено кеширање последњег нивоа кључно за доследност

Без овог глобалног кеша последњег нивоа, свако језгро би морало директно да проверава конзистентност у односу на главну меморију . Сваки пут када би се линија меморије у приватном кешу изменила, било би потребно проверити да ли друга језгра одржавају копију те исте линије и, ако је тако, ажурирати је или поништити свуда.

У систему са много језгара, ово оптерећење проверама би резултирало огромним бројем трансакција у RAM меморији , негирајући велики део предности брзих кеш меморија. Постављањем заједничке кеш меморије између језгара и меморије, процесор може концентрисати контролу кохерентности на једној међулокацији.

У многим имплементацијама, кеш меморије на вишим нивоима (даље од процесора) садрже копије линија присутних на нивоима ближим језгру . Са овом организацијом, протокол кохеренције треба само да осигура да је последњи ниво синхронизован са главном меморијом и да су приватни нивои сваког језгра синхронизовани са нивоом непосредно изнад њега.

Ово се може визуализовати као нека врста руске лутке за гнежђење: кеш меморија трећег нивоа укључује садржај другог и првог нивоа , други ниво укључује свој садржај и садржај првог нивоа, а први ниво познаје само своје редове. Дакле, контролисањем „велике лутке“ (последњег нивоа), систем може ефикасније координирати остатак.

Резултат је да одржавање конзистентности постаје економичније у смислу дизајна и меморијског саобраћаја . Уместо да приморава свако језгро да стално користи RAM, протокол ради на дељеном кешу и одатле управља који редови треба ажурирати или поништити у приватним кешовима.

Методе ажурирања: поништавање и ажурирање копија

Критичан проблем настаје када два или више језгара желе да приступе, готово истовремено, истој линији података која се реплицира у више кеш меморија . У овом контексту, системи за конзистентност обично користе две основне стратегије приликом руковања писањем.

Прва метода се заснива на поништавању. Када језгро треба да пише у одређену кеш линију, протокол поништава све копије те исте линије које могу постојати у другим кеш меморијама . Само језгро које ће писати држи линију у стању за читање и писање; остали, ако желе поново да користе те податке, мораће поново да учитају линију са вишег нивоа (или из меморије) са ажурираном верзијом.

Друга стратегија укључује ажурирање. У овом случају, када језгро измени линију, систем покушава да аутоматски прошири нови садржај на постојеће копије у другим кеш меморијама . На овај начин, сви кеш меморији који су сачували ту линију добијају ажурирану верзију без потребе да је касније пониште и поново учитају.

Сваки приступ има своје предности и мане. Поништавање је обично ефикасније када су писања честа јер избегава засићење меморијског система ажурирањима која другим језгрима можда нису одмах потребна. Супротно томе, ажурирање може бити предност када многа језгра често читају исте податке који се релативно ретко мењају , јер смањује латенцију тако што не мора поново да учитава линију након сваког поништавања.

У оба случаја, обе методе користе додатна стања и контролне битове у кеш линијама. Свака линија обично садржи информације о томе да ли се њен садржај подудара са садржајем у RAM меморији и да ли је дељена, модификована, ексклузивна, резервисана итд., у зависности од специфичног протокола (MESI, MOESI, MSI итд.). Ово омогућава хардверу да брзо доноси одлуке о томе шта да ради када се операција читања или писања догоди на већ реплицираној линији.

  Комплетан водич за оптимизацију БИОС-а и побољшање перформанси рачунара

Провера конзистентности између кеша и меморије

Директна провера конзистентности између свих нивоа кеш меморије процесора или графичке картице и главне меморије био би огроман задатак, како у погледу сложености дизајна, тако и у погледу трошкова перформанси. Стога, модерни системи организују ову верификацију хијерархијски.

Кеш меморије најближе процесору (L1, L2) обично нису директно повезане са RAM меморијом, већ са следећим нивоом кеша. То значи да се конзистентност не проверава у односу на главну меморију на сваком нивоу, већ у односу на непосредно виши ниво . Ово смањује број приступа RAM меморији и поједностављује логику потребну на нижим нивоима.

На крају крајева, поређење садржаја кеш меморије и садржаја РАМ меморије врши се између кеш меморије последњег нивоа и главне меморије . Ако овај последњи ниво одржава исправно и конзистентно стање, а сваки нижи ниво одржава конзистентност са оним изнад њега, цела хијерархија остаје конзистентна без потребе за вишеструком провером сваке линије у односу на РАМ меморију.

Када језгро уписује у кеш линију и мења њене податке, стање те линије се обележава тако да се она више не подудара тачно са копијом сачуваном у меморији . Одатле, протокол координира ажурирање: означава одговарајуће копије у другим кеш меморијама као резервисане или неважеће и, када је то прикладно, уписује нови садржај у придружену главну меморијску линију.

Ова каскадна организација омогућава променама да се прогресивно шире од језгра, које ажурира податке, до главне меморије, пролазећи кроз сваки ниво кеша на контролисан начин. На овај начин, одржавање конзистентности не постаје непремостиво уско грло за процесор.

Кохерентност хардвера наспрам кохерентности софтвера

До сада смо разговарали о механизмима конзистентности који су углавном имплементирани у хардверу: протоколи, статусни битови, дељени кешови итд. Међутим, постоји још један приступ који настоји да део те сложености пребаци на софтвер , тачније на компајлер и оперативни систем.

Шеме конзистентности засноване на софтверу покушавају да смање потребу за додатном логиком на чипу анализирањем кода и доношењем одлука током компајлирања . Идеја је да ако компајлер може да закључи када и како се приступа одређеним дељеним подацима, у многим случајевима би могао да спречи кеширање тих података или да експлицитно управља њиховом видљивошћу.

Овај приступ има јасну предност: део радног оптерећења се пребацује са извршавања на решавање током компајлирања . Уместо да хардвер детектује и обрађује све конфликте у ходу, компајлер покушава да их предвиди и генерише код који избегава опасне ситуације.

Мана је што је статичка анализа кода ограничена, па компајлери имају тенденцију да буду конзервативни . То значи да, како би избегли кршење конзистентности, често доносе одлуке које смањују ефикасност кеш меморија. Ако посумњају да би неки подаци могли бити проблематични, често спречавају њихово кеширање или форсирају синхронизације чешће него што је строго потребно.

Стога, иако су ове софтверске шеме атрактивне у теорији, посебно због поједностављења дизајна хардвера, у пракси оне не замењују подршку за кохерентност интегрисану у сам процесор , већ је допуњују у неким специфичним сценаријима.

Улога компајлера у конзистентности кеша

Кључни елемент софтверски заснованих приступа конзистентности је улога компајлера. Компајлер може да изврши дубинску анализу кода и утврди које дељене структуре података могу бити небезбедне за кеширање . На основу тога, он обележава те елементе на посебан начин или прилагођава генерисање кода.

Најједноставнији, а уједно и најконзервативнији, приступ је спречавање кеширања променљивих дељених података . То јест, сваки приступ овим променљивим приморава приступ главној меморији или области која се не може кеширати. Ово гарантује конзистентност, али пропушта многе могућности за побољшање перформанси, јер се дељена структура, заправо, може користити приватно током одређених периода или само за читање у другим.

  Како променити и надоградити Ви-Фи картицу у вашем старом лаптопу

У стварности, проблем конзистентности настаје само током интервала када барем један процес може да пише у променљиву, а други процес може да је чита . Ван ових критичних периода, променљива се може третирати као да је за искључиву употребу једне нити или чак као ефективна константа на неко време, што омогућава њено кеширање без проблема.

Најнапредније стратегије компајлирања покушавају да идентификују оне „безбедне“ периоде током којих се дељена променљива може сматрати неконфликтном . Да би то урадио, компајлер анализира путање извршавања, потенцијалне истовремене приступе и обрасце синхронизације (закључавања, критичне секције итд.). На основу ове анализе, он дели животни век променљиве на фазе: неке погодне за кеширање, друге које захтевају посебно руковање.

Током критичних периода, када се открије истовремени приступ са писањем, компајлер убацује додатне инструкције у генерисани код како би се спровела конзистентност кеша . Ове инструкције могу приморати пражњење кеша, поновно учитавање меморије, стварање меморијских баријера или приступ регионима означеним као некеширани, у зависности од програмског модела и основне архитектуре.

Однос између компајлера, оперативног система и хардвера

Фраза „компајлер убацује инструкције у генерисани код како би спровео конзистентност кеша“ могла би навести на помисао да оперативни систем чита ове инструкције као да су назнаке високог нивоа и на основу тога одлучује како да изврши програм. У стварности, механизам је донекле другачији.

Када компајлер дода ове типове инструкција, оно што уводи у бинарни фајл су специфичне операције које подржава архитектура или окружење за извршавање . На пример, може да убаци инструкције за пражњење кеша, меморијске баријере, посебне инструкције за означавање региона као некешираних или позиве сервисима оперативног система који конфигуришу атрибуте меморије.

Оперативни систем не тумачи ове инструкције као „коментаре“ или „назнаке“ високог нивоа које је написао компајлер; он једноставно извршава машински код као и било који други . Међутим, неке од ових инструкција су дизајниране да интерагују са меморијским подсистемом и управљањем кеш меморијом, чиме се мења начин на који процесор приступа одређеним подацима.

Другим речима, компајлер врши прелиминарну анализу и генерише код који, када се изврши, производи жељено понашање кеша . Оперативни систем сарађује тако што успоставља атрибуте меморије (кеширане или некеширане области, политике писања итд.) и обезбеђује примитиве синхронизације, али не „чита“ посебне инструкције у смислу њиховог семантичког тумачења као што би то учинио компајлер.

Такође се може десити да хардвер, након што види одређене инструкције, активира специфичне механизме кохеренције или синхронизације . На пример, инструкције за ограду или баријеру гарантују редослед приступа меморији и спроводе одређене ефекте видљивости у хијерархији кеша. У овом случају постоји тросмерна сарадња: компајлер одлучује где да постави ове инструкције, оперативни систем конфигурише окружење за извршавање, а хардвер имплементира стварно понашање на нивоу кеша и меморијске магистрале.

Заједно, сви ови елементи осигуравају да, чак и са више копија истих података распоређених по различитим кеш меморијама и главној меморији, паралелни програми раде са конзистентним моделом меморије . Кохерентност кеша, далеко од тога да је једноставан интерни детаљ процесора, постаје централна компонента за поуздан и ефикасан рад вишејезгарних система.

Разумевање како се хијерархија кеша, протоколи хардверске кохерентности и технике софтверске подршке комбинују јасније чини зашто модерни дизајни процесора деле тако сличну структуру и зашто мали квар у било ком од тих механизама може покренути хаотично понашање у истовременим апликацијама које у потпуности зависе од тога да сва језгра виде исте податке у право време.