17 февруари 2016 г.

Как работи процесорът?

Преди да разгледаме различните хитрости, използвани с цел ускоряване на работата на процесора, нека обърнем за кратко внимание на няколкото прости стъпки, чрез които той работи. Казано най-просто, същността на работата на всеки един процесор (дори графичните) се състои в последователното изпълняване на отделни инструкции, обединяването на които съставлява програмния код като цяло. Самите инструкции могат да притежават най-разнообразна форма, като се започне от елементарното събиране на стойностите, намерени в посочен в инструкцията адрес от паметта, до делението им или преход към друг адрес, където се намира следващата инструкция. Като цяло операциите са разделени в 4 стъпки, като схемата е валидна за 99% от процесорите, използващи архитектура на фон Нюман. Тези стъпки са следните: извличане на инструкцията (fetch), декодиране, изпълняване и обратен запис на резултатите към I/O модула.

Първата стъпка – извличане на инструкцията, се осъществява от специален модул, занимаващ се единствено с тази задача. Адресът в паметта, откъдето той извлича данните, записан в кода на самата програма, който от своя страна буквално се следи от програмен брояч (ПБ). 
Положението на процесора в течащата програма се управлява и контролира от ПБ, като след всяка извършена операция той прави стъпка напред към следващия цикъл. 

След като инструкцията бъде извлечена от паметта, тя се предава към модула, занимаващ се с нейното декодиране. Декодиращият модул разделя инструкцията на няколко части, които се подават на съответния Arithmetic Logic Unit (ALU) модул за изпълнение. Във фрагментите на самата инструкция се съдържат както операциите, които следва да се извършат от ALU, данните, с които да се работи, така и адресите, в които да се запишат готовите резултати.

При третата стъпка – изпълнение на кода, модулите ALU или FPU (Floating Point Unit) изпълняват посочените в инструкцията действия, след което при последната, 

Четвърта стъпка записват резултатите в посочените в инструкцията координати. За ускоряването на тези 4 стъпки има много варианти, всеки от които пряко свързан с тясното място на фон Нюмановата архитектура. Първият и най-широко навлязъл метод е въвеждането на конвейерното изпълнение на инструкциите (pipelining).


МАЛКО ПО-ОПРОСТЕНО:


Ето какво трябва да знаете: Процесорът изпълнява три главни задачи: чете данните, обработва ги и често пише данните в паметта. Суровата скорост се измерва в мегахерци, но и други фактори, като дизайнът например, са важни при определянето на производителността на един чип. За да изберете чипа, който искате да пъхнете в компютъра си ще трябва да прецените за какво ще използвате компютъра и колко можете да си позволите да платите. Тухла по тухла Преди да разберете как работи процесора, ще трябва да знаете от какво е направен. Процесорът представлява милиони микроскопични транзистори, които са проядени с помощта на химични и фотолитографски процеси в полирана силиконова пластина с големина на човешки нокът.

Тези малки транзистори съхраняват електрически заряди, които отговарят на нули или единици, изграждащи двоичният език, който компютрите използват за комуникация. Групи от транзистори се свързват заедно, за да съхраняват по-големи стойности; също така извършват математически и логически изчисления и с помощта на кварцов часовник постъпково синхронизират своите функции като синхронни плувци. С други думи, тези транзистори обработват данни. Процес на обработка Процесорът обработва информацията, която се съхранява в байтовете на паметта. Тази информация може да бъде във вид на данни или инструкции.

Данните са двоично представени цифри, числа или цветове, например. Инструкциите казват на процесора какво да прави с данните: да ги събере, извади или премести, например. Процесорът осъществява три основни операции с данните: чете, обработва и често пише тези данни в паметта. На най-ниско ниво, процесорът има нужда само от четири основни елемента за да проведе операциите: инструкции, показател на инструкции, регистри и аритметичен логичен модул. Показателят на инструкции казва на процесора, къде в паметта се намира инструкцията, която трябва да изпълни. Регистрите на процесора са участъци за временно съхраняване.

Те съдържат данни, които чакат да бъдат обработени с дадена инструкция или данни, които са вече обработени (например, резултатът от събиране на две числа). Аритметичният логичен модул (ALU) работи като калкулатор, изпълнявайки математическите и логически функции, диктувани от инструкциите. Процесорът се състои от няколко допълнителни части, които помагат на основните модули да си вършат работата: Доставчикът на инструкции (instruction fetch) “хваща” инструкции от RAM-а или от област на паметта, намираща се в процесора. Декодерът на инструкции (instruction decoder) взима инструкцията от доставчика и я превежда така, че процесорът да я разбере.

След това декодерът определя колко стъпки са необходими за нейното изпълнение. Контролният модул (control unit) управлява и координира всички операции на чипа. Той казва на ALU кога да смята, казва на доставчика кога да достави някоя стойност и на декодера, кога да превърне стойността в дадена инструкция. Проследяване на инструкции Ето как става: показателят на инструкции насочва доставчика на инструкции към място в паметта, което съдържа инструкция. Доставчикът “хваща” инструкцията и я предава на декодера на инструкции, който я изучава и определя колко стъпки са необходими за нейното изпълнение.

(Една инструкция може да се състои от много стъпки, които трябва да бъдат изпълнени в специфичен ред). ALU след това изпълнява работата, която се изисква от инструкцията: събира, изважда или с други думи – обработва данните. След като процесорът интерпретира и изпълни инструкцията, контролният модул казва на доставчика да донесе следващата инструкция от паметта. Този процес продължава – инструкция след инструкция, с умопомрачаваща скорост и в крайна сметка виждате резултата на вашия монитор. Всъщност това са програмите (текстов редактор, например): съвкупност от инструкции и данни. За да може всичко това да става навреме е нужен часовников генератор.

Той регулира всяка от функциите, които процесорът изпълнява. Като метроном, часовниковият генератор изпраща импулси, които определят стъпката за процесора. Импулсите се измерват в милиони цикли в секунда или мегахерци – позната ви вече величина за чистата/суровата скорост на процесора. Повече импулси – по-бърз процесор. При равни условия, един 700-мегахерцов процесор винаги ще е по-бърз от 600-мегахерцов, но различните добавки и вариации при процесорите правят тези стойности да изглеждат по-малко вероятни. Разширяване на основните функции на процесора Процесорът има нужда само от няколко основни елемента за да работи, но основният дизайн се променя с течение на времето за да се увеличи общата производителност.

Целта на тези промени е една и съща: по-бърза обработка на данните. Изпробвайки нови начини да забързат обработката, производителите на чиповете забелязаха, че процесорът не работи, докато чете инструкции или данни от системната памет. За да се намали изчакването, те поставиха памет вътре в процесора, която се нарича кеш-памет. По този начин, данните и инструкциите могат да бъдат съхранявани временно в процесора, намалявайки броя на разходките до RAM-a. Развивайки идеята за кеша, производителите на системи поставиха бързодействащ (и скъп) RAM (наречен кеш от второ ниво или L2-кеш) между първия кеш на процесора и системната памет.

Голямата близост до процесора означава още по-малко разходки до RAM-a. Този вторичен кеш бе толкова благоприятен за производителността, че скоро много производители го интегрираха в самия процесор, създавайки повече резервоари за данни и инструкции. Повече ALU-та и FPU За да се подобри производителността при изчисленията, производителите на чипове поставиха още един аритметичен логически модул в процесора. Теоретически това означава двойно по-бърза обработка. В добавка към ALU-тата, Intel интегрира модул за изчисления с плаваща запетая (FPU) в процесора. FPU се занимава с прекалено малките или прекалено големите числа (числата с много десетични позиции).

Докато FPU се занимава с този вид изчисления, ALU е свободен за други задачи, което също повишава производителността. Advanced Micro Devices (AMD) и Intel забързаха обработката на инструкциите като добавиха конвейрна обработка на инструкциите или с други думи изпълняването им почти паралелно, застъпвайки се една друга. Изпълнението на дадена инструкция предполага няколко отделни стъпки – доставка и декодиране на инструкция, например. Първоначално процесорът трябваше да завършва напълно изпълнението на инструкция, преди да премине към следващата. Сега дискретни вериги се занимават с отделните стъпки.

След като инструкцията се е придвижила от първата стъпка към втората, транзисторите които са се занимавали с първата стъпка са свободни за следващата инструкция, по този начин ускорявайки процеса. Това прилича на изкачване на стълби: след като сте изкачили стълбата – тя е свободна за човека, идващ след вас. Други добавки, увеличаващи производителността включват предвиждане на разклоненията в кода – оценка на това, по кой клон е по-вероятно да мине изпълнението на програмата; спекулативно изпълнение - изпълнение на предвиден клон предварително; извънредно изпълнение – възможността да се изпълнят редица инструкции извън обичайния ход на програмата.



След като се запознахте бегло с 4те стъпки, вижте този клип, който може да забавите на места: Как функционира един процесор




Инструкции на конвейeр – NetBurst
Един от най-ярките представители на доведената до крайност конвейeрна архитектура е именно дизайнът на процесорите на Intel от предишното поколение – става въпрос именно за моделите, използващи т.нар. NetBurst. Този дизайн е следствие на един от опитите за избягване на тясното място в комуникацията между процесора и останалите елементи на системата, в частност паметта.
За да представим нагледно идеята за конвейeрното изпълнение на инструкциите в процесора, най-просто можем да си представим ситуацията по следния начин: преминаването на инструкцията през 4-те основни стъпки изисква 4 такта от честотата на процесора. В случай че не разполагаме с конвейeрно изпълнение, във всеки един от 4-те такта процесорът е изцяло зает с една-единствена инструкция, макар и от 4-те му модула (fetcher, decoder, ALU/FPU, writeback) да се използва само по един във времето. След като инструкцията бъде извлечена и предадена на декодера, извличащият модул трябва да изчака 3 такта, докато инструкцията премине по цялата верига, преди да извлече следващата порция. Въвеждането на конвейeр елиминира този момент, като във 2-рия такт от 4-те стъпки извличащият модул прихваща следващата порция от данните. Тъй като в 3-тия такт декодиращият модул вече е предал инструкцията за изпълнение към двойката ALU/FPU, той е свободен да приеме извлечената във 2-рия такт инструкция.
Обикновено дълбочината на конвейeра в различните процесорни архитектури варира, като в последните поколения се движи от 10 за Pentium 3 до невероятните 31 за последния представител на семейство NetBurst, използващ ядро Prescott. Именно повишеният брой на стъпките в конвейeра на Prescott позволява на Intel да извиси тактовата му честота до невероятните 3+GHz. Най-просто казано, по-дългият конвейeр позволява по-безпроблемна комуникация между модулите, занимаващи се с обмена на данните между процесора и външните елементи.
За съжаление удължаването на конвейeра има и един много голям недостатък, свързан с модула, занимаващ се с предсказването на бъдещите операции (branch prediction unit). Поради увеличената дължина на конвейера изпълнението на излишна инструкция ще забави процесора с повече време, Intel усъвършенстват алгоритмите на работа на модула за предсказване на преходите. Въпреки това грешка в неговата работа означава сериозна загуба на процесорно време, тъй като, колкото е по-дълъг конвейерът, при появата на грешка или излишни операции се налага изчистването и повторното въвеждане на данни в толкова повече клетки.
Реално погледнато, единствените области, в които един процесор може да се възползва без проблем от конвейер със значителна дълбочина на стъпките, е кодирането или декодирането на мултимедиа. Тъй като там branch prediction модулът има огромна успеваемост на своята работа, процесорът работи при почти 100% заетост на своя конвейер, което се изразява в значително по-малкото на брой излишни нейни цикли. За съжаление в реалната употреба подобно приложение е силно ограничено, което, както се вижда, се отрази негативно на NetBurst подхода. Това се дължи на факт, че в приложения от сорта на 3D моделиране или игри преходите и операциите са значително по-разнообразни, вследствие на което branch prediction модулът не постига добра успеваемост на предсказанията си.
Тук именно се корени и една от най-големите разлики между процесорите от тези години – макар моделите на AMD да бяха с почти два пъти по-ниска тактова честота, тяхната производителност беше равна или дори по-висока от тази на моделите на Intel. Причината за това се крие в по-краткия брой стъпки в конвейера на K7, наброяващи едва 10 за ALU и 15 за FPU модула. Благодарение на по-малкото стъпки в конвейера процесорите с К7 архитектура могат да изпълняват повече инструкции за такт, макар и при по-ниска тактова честота. В допълнение към това една евентуална грешка на branch prediction модула не би имала толкова голямо влияние върху производителността поради по-малкото на брой грешни стъпки, които ще се извършат от процесора.


Суперскаларни процесори
Суперскаларните процесори представляват първата стъпка към въвеждането на паралелната обработка на данни, по-късно прерастваща в добавянето на още ядра в кристала на чипа. На практика суперскаларната работа на един процесор означава, че той е способен да работи едновременно с повече от една инструкция за такт. Макар да е в пряко отношение към конвейерната обработка на инструкциите, суперскаларността е напълно отделна от нея технология и не бива да се бъркат двете техники. Обикновено суперскаларното изпълнение на инструкциите се постига чрез добавянето на определени допълнителни модули (но не цялостно дублиране на целия процесор), които могат да се използват и за други цели.
 
По време на работа изпълнението на повече инструкции се осъществява, като се използват модули, които за момента не са активни, макар и да имат друго предназначение. Като подобен случай може да се посочи използването на математическия копроцесор за изпълнение на някои операции с цели числа. В някои случаи това позволява постигането на суперскаларна архитектура почти без добавяне на допълнителни модули освен изискващите се за контрол на операциите и проверка на зависимостите (което е от голямо значение).
Въвеждането на суперскаларната работа на процесорите предизвиква парадокс от гледна точка на изпълняващия се на тях програмен код. В подобен случай става възможна ситуация, при която резултатите от инструкция, изпълнявана на единия конвейер на суперскаларен процесор, да са в противоречие с тези на данните от втория конвейер. Подобен случай би могъл да причини доста главоболия на програмистите, ето защо в процесора са въведени специфични модули, следящи за зависимостите между различните инструкции и резултатите от тяхното изпълнение, както и обезпечаващи правилното записване на данните в паметта, за да се избегне припокриване.
На теория въвеждането на суперскаларността би трябвало да повиши поне 2 пъти производителността на процесорите, но както често се случва в практиката, това съвсем не е така. За да може напълно да се използват възможностите на един суперскаларен чип, изпълняваният на него софтуер трябва да е предвиден и написан специално за тази цел. За съжаление това е невъзможно, тъй като, за да се запази обратната съвместимост с по-старите модели процесори, програмите не могат да се напишат по такъв начин.
Кеш памет
Както вече споменахме в началото, един от основните недостатъци на архитектурата на фон Нюман е наличието на тясно място в комуникацията между процесора и паметта на системата. Тъй като употребата на изключително бърза памет със скорост, равна на тази на процесора, е неоправдано от икономическа гледна точка, производителите разработват друга система – кеш паметта. Тя представлява изключително бърза памет с малък обем, намираща се на кристала на самия процесор (за сегашните и близки процесорни поколения), работеща със същата тактова честота като него.
Според вида и предназначението си кеш паметта може да е от първо (L1), второ (L2) или трето (L3) ниво, но нейната задача винаги е една и съща – да предотврати изчакването на процесора, докато изисканите от него данни се доставят от бавната системна памет. Разбира се, в кеш паметта не се съхраняват същинските данни, а инструкциите, опериращи с тях. Според вида и логиката на организация на кеша тя може да задържа вече използваните инструкции определен брой процесорни цикли, тъй като те биха могли да потрябват отново. В допълнение към това кеш паметта е в пряко взаимодействие с branch prediction модула – инструкциите, които според него се очаква да бъдат изпълнени скоро, биват извличани предварително, а данните от тях се записват в кеш паметта на процесора.
Бъдещето
Както стана ясно от изоставянето на NetBurst в полза на Core 2 от страна на Intel, бъдещето явно ще бъде не в увеличаване на тактовата честота за сметка на по-дълбок конвейер, а в повишаването на паралелизма на изпълнение. Въвеждането на суперскаларни процесори, едновременно изпълняващи 4 или повече инструкции за такт, заедно с повишаването на броя ядра в системата сочи недвусмислено пътя, по който ще поемат производители като Intel и AMD. За момента Intel отново притежава водещото място по отношение на производителността на процесорите, тъй като комбинирането на опита, придобит от NetBurst, с по- късия конвейер доведе до наистина впечатляващ скок в производителността. Очакваният след няколко месеца Nehalem ще покаже какви са възможностите на моделите с 4 ядра при 45nm технология, а с какво ще отговори AMD можем само да гадаем. Във всеки случай посоката е ясна – повишаване на броя паралелно изпълнявани инструкции, остава да се изясни само начинът за постигането му.

Няма коментари:

Публикуване на коментар