Vi tenker alle på CPU-en som "hjernen" til en datamaskin, men hva betyr det egentlig? Hva skjer med milliarder transistorer for at datamaskinen din skal fungere? I denne nye firedelte miniserien vil vi fokusere på maskinvaredesign som omfatter innganger og utganger til det som kjører en datamaskin.

Denne serien vil dekke dataarkitektur, prosessorkretsdesign, VLSI (veldig storskalaintegrasjon), brikkeproduksjon og fremtidige trender innen databehandling. Hvis du alltid har vært interessert i detaljer om hvordan prosessorer fungerer inne, kom i gang, for det er det du vil vite for å komme i gang.

Vi starter på et veldig høyt nivå med hva en prosessor gjør og hvordan byggesteiner passer sammen i et fungerende design. Dette inkluderer prosessorkjerner, minnehierarki, forutsigelse av grenen og mer. Først trenger vi en grunnleggende definisjon av hva CPU-en gjør. Den enkleste forklaringen er at en CPU følger et sett med instruksjoner for å operere på et sett med innganger. For eksempel kan dette være å lese en verdi fra minnet, og deretter legge den til en annen verdi og til slutt lagre resultatet på et annet sted. Det kan også være noe mer komplekst, som å dele to tall, hvis resultatet av den forrige beregningen er større enn null.

Når du vil kjøre et program som et operativsystem eller et spill, er selve programmet et sett med instruksjoner som CPUen skal utføre. Disse instruksjonene lastes fra minnet og i en enkel prosessor og utføres en etter en til programmet er ferdig. Mens programvareutviklere skriver programmene sine på høyt nivå språk som C ++ eller Python, kan ikke prosessoren forstå dette. Den forstår bare 1 og 0, så vi trenger en måte å representere koden i dette formatet.




Programmene er samlet i en rekke instruksjoner på lavt nivå. monteringsspråk Som en del av Instruction Set Architecture (ISA). Dette er settet med instruksjoner CPU er bygget for å forstå og utføre. Noen av de vanligste ISAene er x86, MIPS, ARM, RISC-V og PowerPC. Akkurat som syntaksen for å skrive en funksjon i C ++ er forskjellig fra en funksjon som gjør det samme i Python, har hver ISA en annen syntaks.




Disse ISA-ene kan deles inn i to hovedkategorier: fast lengde og variabel lengde. RISC-V ISA bruker instruksjoner med fast lengde, noe som betyr at den bestemmer hvilken type kommando som er et bestemt antall forhåndsdefinerte biter i hver kommando. Dette er forskjellig fra x86 ved bruk av instruksjoner med variabel lengde. I x86 kan instruksjonene kodes på forskjellige måter og med forskjellige antall bits for forskjellige deler. På grunn av denne kompleksiteten er kommandodekoderen på x86-CPUer vanligvis den mest komplekse delen av hele designet.

Instruksjoner med fast lengde muliggjør enklere dekoding på grunn av deres normale natur, men begrenser det totale antallet instruksjoner en ISA kan støtte. Vanlige versjoner av RISC-V-arkitekturen har omtrent 100 direktiver, og selv om den er åpen kildekode, er den x86 proprietær, og ingen vet hvor mange direktiver det er. Folk tror vanligvis det er flere tusen x86-instruksjoner, men det nøyaktige antallet er ikke offentlig tilgjengelig. Til tross for forskjellene mellom ISA-er, har de hovedsakelig den samme grunnleggende funksjonaliteten.







Nå er vi klare til å slå på datamaskinen og starte noe. Utførelsen av en kommando har faktisk noen få grunnleggende deler som brytes ned fra mange trinn i en prosessor.




Det første trinnet er å bringe instruksjonene fra minnet til CPUen for å starte kjøringen. I det andre trinnet dekodes kommandoen slik at CPU-en kan forstå hva slags instruksjon det er. Det er mange typer som aritmetiske kommandoer, greninstruksjoner og minneinstruksjoner. Når CPU-en lærer seg hva slags instruksjon den utfører, blir instruksjonens operander samlet fra minne eller interne registre i CPU-en. Hvis du vil legge til tallet A i B-nummeret, kan du ikke legge til uten å vite A- og B-verdiene. De fleste moderne prosessorer er 64 bits, noe som betyr at størrelsen på hver dataverdi er 64 bits.




Etter at CPU-en har operandene for instruksjonen, beveger den seg til utførelsesfasen der behandlingen utføres ved inngang. Dette kan være å legge til tall, lage en logisk manipulasjon på tallene, eller bare passere tallene uendret. Når resultatet er beregnet, kan det være behov for tilgang til minne for å lagre resultatet, eller CPU kan beholde verdien i et av sine interne registre. Etter at resultatet er lagret, vil CPU oppdatere statusen til de forskjellige elementene og gå videre til neste instruksjon.

Denne forklaringen er selvfølgelig en stor forenkling, og de fleste moderne prosessorer deler disse få trinnene i 20 eller færre trinn for å øke effektiviteten. Dette betyr at mens prosessoren vil starte og fullføre flere instruksjoner i hver syklus, kan enhver instruksjon kreve 20 eller flere sykluser å fullføre fra start til slutt. Dette mønsteret blir ofte referert til som en rørledning fordi det tar litt tid å fylle rørledningen og væsken vil passere helt gjennom, men når den er fylt vil du få en jevn utgang.

Hele syklusen som en instruksjon går gjennom er en veldig streng koreografert prosess, men ikke alle instruksjoner kan slutte samtidig. For eksempel kan innsetting være veldig rask, mens deling eller lasting fra minne kan ta hundrevis av sykluser. I stedet for å stoppe hele prosessoren når en langsom instruksjon er ferdig, går de fleste moderne prosessorer ute av drift. Dette betyr at de vil bestemme hvilken kommando som vil være mest nyttig å utføre på et bestemt tidspunkt og buffer andre instruksjoner som ikke er klare. Hvis den nåværende instruksjonen ikke er klar ennå, kan prosessoren hoppe fremover i koden for å se om noe annet er klart.

I tillegg til den uvanlige utførelsen, har typiske moderne prosessorer, superscalar arkitektur. Dette betyr at prosessoren til enhver tid utfører mange instruksjoner samtidig på hvert trinn av rørledningen. I tillegg kan hundrevis av andre vente på å begynne henrettelsen. For å kunne utføre mange instruksjoner på en gang, vil de ha flere eksemplarer av hvert rørledningsstadium i prosessorene. Hvis en prosessor ser at de to kommandoene er klare til å kjøres, og i stedet for å vente på at de skal fullføres separat, hvis det ikke er noen avhengighet mellom dem, kjører den begge samtidig. En vanlig anvendelse av dette kalles Simultaneous Multithreading (SMT), også kjent som Hyper-Threading. Mens Intel- og AMD-prosessorer for tiden støtter toveis SMT, har IBM utviklet brikker som støtter åtteveis SMT.

For å oppnå denne nøye koreografiske utførelsen har en prosessor mange ekstra elementer i tillegg til den grunnleggende kjernen. Det er hundrevis av separate moduler i en prosessor, som hver tjener et bestemt formål, men vi vil dekke det grunnleggende. De to største og mest nyttige er cache og gren prediktorer. Ytterligere strukturer vi ikke vil dekke inkluderer ting som omorganisering av buffere, postalias-tabeller og reservasjonsstasjoner.

Formålet med cacher kan være forvirrende da de ofte lagrer data som RAM eller SSD. Det som skiller cacher er tilgangstider og hastigheter. Selv om RAM er ekstremt raskt, er det veldig langsom størrelsesorden for CPU. RAM kan ta hundrevis av sykluser for å svare med data, og prosessoren henger og gjør ingenting. Hvis dataene ikke er i RAM, kan det ta titusenvis av sykluser for å få tilgang til data på en SSD. Uten cacher stoppet prosessorene våre.

Prosessorer har vanligvis tre cache-nivåer. minnehierarki. L1 cache er den minste og raskeste, L2 er i midten, og L3 cache er den største og tregeste. Over cachene i hierarkiet er det små poster som lagrer en enkelt dataverdi under beregningen. Disse postene er i størrelsesorden de raskeste lagringsenhetene i systemet ditt. Når en kompilator konverterer høynivåprogrammet til kompileringsspråket, bestemmer det den beste måten å bruke disse registerene på.

Når CPU ber om data fra minnet, sjekker den først om dataene er lagret i L1-hurtigbufferen. I så fall kan du få tilgang til data på bare noen få sykluser. Hvis den ikke er tilgjengelig, kontrollerer CPU L2 og ser etter L3-hurtigbufferen. Cacher implementeres vanligvis transparent til kjernen. Kjernen vil bare be om noen data på en bestemt minneadresse og vil svare på ethvert nivå i hierarkiet. Størrelse og ventetid øker vanligvis i størrelsesorden når vi går videre til de senere stadiene i minnehierarkiet. Til slutt, hvis prosessoren ikke finner dataene den leter etter i noen av hurtigbufferne, bare da vil den gå til hovedminnet (RAM).

I en typisk prosessor vil hver kjerne ha to L1-cacher: en for data og en for instruksjoner. L1-cacher er omtrent 100 kilobyte totalt, og størrelsen kan variere avhengig av brikke og generasjon. Hver arkitektur har vanligvis en L2-cache, men noen arkitekturer kan deles mellom to kjerner. L2-cacher er vanligvis flere hundre kilobyte. Til slutt er det en enkelt L3-cache som er rundt titalls megabyte delt på tvers av alle kjerner.

Når en prosessor utfører kode, blir instruksjonene og dataverdiene den bruker oftest, bufret. Dette øker kjøringen betydelig, ettersom prosessoren ikke trenger å stadig gå til hovedminnet for de dataene den trenger. Vi vil snakke mer om hvordan disse minnesystemene faktisk er implementert i andre og tredje del av denne serien.

Foruten cacher, er en av de andre viktige byggesteinene til en moderne prosessor riktig gren prediktor. Greninstruksjoner ligner "if" -uttalelser fra en prosessor. Hvis vilkåret er sant, blir et sett med kommandoer utført, og hvis tilstanden er falsk, blir en sekvens av kommandoer utført. Det kan for eksempel være lurt å sammenligne to tall, og hvis de er like, kan du utføre en funksjon, og hvis forskjellige, kan du utføre en annen funksjon. Disse greninstruksjonene er ekstremt vanlige og kan utgjøre omtrent 20% av alle instruksjonene i et program.

På overflaten kan det hende at disse greninstruksjonene ikke virker som et problem, men det kan være veldig vanskelig å få en prosessor riktig. Det er veldig viktig å vite, siden CPU til enhver tid kan være i ferd med å utføre ti eller tjue instruksjoner om gangen hvilken instruksjoner for gjennomføring. Det kan ta 5 sløyfer for å avgjøre om den gjeldende kommandoen er en gren og ytterligere 10 sløyfer for å avgjøre om tilstanden er oppfylt. I løpet av denne tiden kan prosessoren ha begynt å utføre dusinvis av tilleggsinstruksjoner uten å vite om de var riktige instruksjoner.

For å løse dette problemet bruker alle moderne høyytelsesprosessorer en teknikk som kalles spekulasjon. Dette betyr at prosessoren vil følge greninstruksjonene og forutsi om filialen vil bli anskaffet eller ikke. Hvis gjetningen er riktig, har prosessoren begynt å utføre påfølgende instruksjoner, noe som gir en ytelsesgevinst. Hvis gjetningen er feil, stopper prosessoren kjøringen, fjerner eventuelle gale instruksjoner den startet og starter fra riktig punkt.

Disse grenprediktorene er noen av de tidligste formene for maskinlæring fordi prognosemakeren gradvis lærer oppførselen til grener. Hvis han gjetter for mye galt, begynner han å lære seg riktig oppførsel. Tiår med forskning på teknikker for forutsigelse av grener har resultert i mer enn 90% nøyaktighet i moderne prosessorer.

Mens spekulasjoner gir enorme ytelsesgevinster, avslører det også sårbarheter, selv om prosessoren kan utføre ferdige instruksjoner i stedet for å vente på travle. Det berømte Spectre-angrepet utnytter feil i spådommer og spekulasjoner. Angriperen bruker spesiallaget kode for å tvinge prosessoren til å spekulativt utføre kode som lekker minneverdier. Noen aspekter av spekulasjonene måtte redesignes for å sikre at dataene ikke kunne lekkes, noe som resulterte i et lite fall i ytelsen.

Arkitekturen som er brukt i moderne prosessorer har kommet langt de siste tiårene. Innovasjoner og smart design har resultert i mer ytelse og bedre bruk av underliggende maskinvare. CPU-produsenter er topphemmelige om teknologiene i prosessorene sine, så det er umulig å vite nøyaktig hva som skjer inne. Imidlertid er det grunnleggende om hvordan datamaskiner fungerer standardisert på alle prosessorer. Intel kan legge til sin skjulte saus for å øke cache-hitfrekvensen, eller AMD kan legge til en avansert grenestimator, men begge gjør den samme oppgaven.

Dette første utseendet og oversikten dekket det meste av det grunnleggende om hvordan prosessorer fungerer. I neste avsnitt vil vi diskutere hvordan komponentene som kommer inn i CPUen er designet, som dekker logiske porter, klokke, strømstyring, kretsdiagrammer og mer. Fortsett å se på oss.

Foreslåtte målinger:

Masthead-kreditt: Nærbilde av elektronisk kretskort av Raimuda