Computersystemsimulatorer: den velkendte fuld-platform simulator og den ukendte bar and trace

I anden del af artiklen om computersystemsimulatorer vil jeg fortsætte med at tale i en simpel introduktionsform om computersimulatorer, nemlig om fuldplatformssimuleringen, som den gennemsnitlige bruger oftest støder på, samt om ur-by -urmodel og spor, som er mere almindelige i udviklerkredse.

Computersystemsimulatorer: den velkendte fuld-platform simulator og den ukendte bar and trace

В den første del Jeg talte om, hvad simulatorer er generelt, samt om niveauerne af simulering. Nu, baseret på den viden, foreslår jeg at dykke lidt dybere og tale om fuld-platform simulering, hvordan man indsamler spor, hvad man skal gøre med dem senere, samt om ur-for-ur mikroarkitektonisk emulering.

Fuld platform simulator, eller "Alene i feltet er ikke en kriger"

Hvis du ønsker at studere driften af ​​en bestemt enhed, for eksempel et netværkskort, eller skrive firmware eller en driver til denne enhed, så kan en sådan enhed simuleres separat. Det er dog ikke særlig praktisk at bruge det isoleret fra resten af ​​infrastrukturen. For at køre den tilsvarende driver skal du bruge en central processor, hukommelse, adgang til en databus osv. Derudover kræver driveren et operativsystem (OS) og en netværksstak for at fungere. Derudover kan en separat pakkegenerator og svarserver være påkrævet.

En fuldplatformssimulator skaber et miljø til at køre en komplet softwarestak, som inkluderer alt fra BIOS og bootloader til selve OS og dets forskellige undersystemer, såsom den samme netværksstak, drivere og applikationer på brugerniveau. For at gøre dette implementerer den softwaremodeller for de fleste computerenheder: processor og hukommelse, disk, input/output-enheder (tastatur, mus, skærm) samt det samme netværkskort.

Nedenfor er et blokdiagram over x58-chipsættet fra Intel. En fuld-platform computersimulator på dette chipset kræver implementering af de fleste af de listede enheder, inklusive dem inde i IOH (Input/Output Hub) og ICH (Input/Output Controller Hub), som ikke er afbildet i detaljer på blokdiagrammet . Selvom der, som praksis viser, ikke er mange enheder, der ikke bruges af den software, vi skal køre. Modeller af sådanne enheder behøver ikke at blive oprettet.

Computersystemsimulatorer: den velkendte fuld-platform simulator og den ukendte bar and trace

Oftest implementeres fuldplatformssimulatorer på processorinstruktionsniveau (ISA, se nedenfor). tidligere artikel). Dette giver dig mulighed for at oprette selve simulatoren relativt hurtigt og billigt. ISA-niveauet er også godt, fordi det forbliver nogenlunde konstant, i modsætning til for eksempel API/ABI-niveauet, som skifter oftere. Derudover giver implementering på instruktionsniveau dig mulighed for at køre såkaldt umodificeret binær software, det vil sige køre allerede kompileret kode uden ændringer, præcis som det bruges på rigtig hardware. Med andre ord kan du lave en kopi ("dump") af din harddisk, angive den som et billede til en model i en fuld-platform simulator, og voila! – OS og andre programmer indlæses i simulatoren uden yderligere handlinger.

Simulator ydeevne

Computersystemsimulatorer: den velkendte fuld-platform simulator og den ukendte bar and trace

Som nævnt lige ovenfor, er processen med at simulere hele systemet, det vil sige alle dets enheder, en ret langsom virksomhed. Hvis du også implementerer alt dette på et meget detaljeret niveau, for eksempel mikroarkitektonisk eller logisk, så vil eksekveringen blive ekstremt langsom. Men instruktionsniveauet er et passende valg og tillader OS og programmer at køre med hastigheder, der er tilstrækkelige til, at brugeren kan interagere med dem komfortabelt.

Her ville det være passende at berøre emnet simulatorydelse. Det måles normalt i IPS (instruktioner per sekund), mere præcist i MIPS (millioner IPS), det vil sige antallet af processorinstruktioner, der udføres af simulatoren på et sekund. Samtidig afhænger hastigheden af ​​simuleringen også af ydeevnen af ​​det system, som selve simuleringen kører på. Derfor kan det være mere korrekt at tale om simulatorens "slowdown" sammenlignet med det originale system.

De mest almindelige fuld-platform simulatorer på markedet, såsom QEMU, VirtualBox eller VmWare Workstation, har god ydeevne. Det er måske ikke engang mærkbart for brugeren, at der arbejdes i simulatoren. Dette sker takket være de særlige virtualiseringsfunktioner implementeret i processorer, binære oversættelsesalgoritmer og andre interessante ting. Dette er alt sammen et emne for en separat artikel, men kort fortalt er virtualisering en hardwarefunktion i moderne processorer, der gør det muligt for simulatorer ikke at simulere instruktioner, men at sende dem til udførelse direkte til en rigtig processor, hvis, selvfølgelig, arkitekturerne af simulatoren og processoren ligner hinanden. Binær oversættelse er oversættelse af gæstemaskinekode til værtskode og efterfølgende eksekvering på en rigtig processor. Som følge heraf er simuleringen kun lidt langsommere, 5-10 gange, og kører ofte endda med samme hastighed som det rigtige system. Selvom dette er påvirket af mange faktorer. For eksempel, hvis vi ønsker at simulere et system med flere dusin processorer, så vil hastigheden straks falde med disse flere dusin gange. På den anden side understøtter simulatorer som Simics i de seneste versioner multiprocessor-værtshardware og paralleliserer effektivt de simulerede kerner til kernerne i en rigtig processor.

Hvis vi taler om hastigheden af ​​mikroarkitektonisk simulering, så er det normalt flere størrelsesordener, omkring 1000-10000 gange langsommere end udførelse på en almindelig computer, uden simulering. Og implementeringer på niveau med logiske elementer er langsommere i flere størrelsesordener. Derfor bruges en FPGA som emulator på dette niveau, hvilket kan øge ydeevnen markant.

Grafen nedenfor viser en omtrentlig afhængighed af simuleringshastighed på modeldetaljer.

Computersystemsimulatorer: den velkendte fuld-platform simulator og den ukendte bar and trace

Beat-for-beat-simulering

På trods af deres lave udførelseshastighed er mikroarkitektoniske simulatorer ret almindelige. Simulering af processorens interne blokke er nødvendig for nøjagtigt at simulere udførelsestiden for hver instruktion. Her kan der opstå misforståelser - når alt kommer til alt, ser det ud til, hvorfor ikke blot programmere udførelsestiden for hver instruktion. Men en sådan simulator vil være meget unøjagtig, da udførelsestiden for den samme instruktion kan variere fra opkald til opkald.

Det enkleste eksempel er en hukommelsesadgangsinstruktion. Hvis den anmodede hukommelsesplacering er tilgængelig i cachen, vil udførelsestiden være minimal. Hvis denne information ikke er i cachen ("cache miss"), vil dette i høj grad øge udførelsestiden for instruktionen. Der kræves således en cache-model for nøjagtig simulering. Sagen er dog ikke begrænset til cachemodellen. Processoren vil ikke blot vente på, at data bliver hentet fra hukommelsen, når den ikke er i cachen. I stedet vil den begynde at udføre de næste instruktioner og vælge dem, der ikke afhænger af resultatet af læsning fra hukommelsen. Dette er den såkaldte "out of order" eksekvering (OOO, out of order execution), der er nødvendig for at minimere processorens inaktive tid. Modellering af de tilsvarende processorblokke vil hjælpe med at tage alt dette i betragtning ved beregning af udførelsestiden for instruktioner. Blandt disse instruktioner, der udføres mens resultatet af læsning fra hukommelsen afventes, kan der forekomme en betinget springoperation. Hvis resultatet af tilstanden er ukendt i øjeblikket, stopper processoren igen ikke udførelsen, men laver et "gæt", udfører den passende gren og fortsætter proaktivt med at udføre instruktioner fra overgangspunktet. En sådan blok, kaldet en grenprædiktor, skal også implementeres i den mikroarkitektoniske simulator.

Billedet nedenfor viser processorens hovedblokke, det er ikke nødvendigt at kende det, det er kun vist for at vise kompleksiteten af ​​den mikroarkitektoniske implementering.

Computersystemsimulatorer: den velkendte fuld-platform simulator og den ukendte bar and trace

Driften af ​​alle disse blokke i en rigtig processor er synkroniseret af specielle clock-signaler, og det samme sker i modellen. Sådan en mikroarkitektonisk simulator kaldes cyklus nøjagtig. Dens hovedformål er nøjagtigt at forudsige ydeevnen af ​​den processor, der udvikles, og/eller beregne eksekveringstiden for et specifikt program, for eksempel et benchmark. Hvis værdierne er lavere end påkrævet, vil det være nødvendigt at ændre algoritmerne og processorblokkene eller optimere programmet.

Som vist ovenfor er ur-for-ur-simulering meget langsom, så den bruges kun, når man studerer bestemte øjeblikke af et programs drift, hvor det er nødvendigt at finde ud af den reelle hastighed for programudførelse og evaluere den fremtidige ydeevne af den enhed, hvis prototypen bliver simuleret.

I dette tilfælde bruges en funktionel simulator til at simulere programmets resterende køretid. Hvordan sker denne kombination af brug i virkeligheden? Først lanceres den funktionelle simulator, hvorpå OS og alt nødvendigt for at køre det undersøgte program indlæses. Vi er trods alt ikke interesserede i selve OS, og heller ikke i de indledende faser af lanceringen af ​​programmet, dets konfiguration osv. Vi kan dog heller ikke springe disse dele over og straks gå videre til at udføre programmet fra midten. Derfor køres alle disse indledende trin på en funktionel simulator. Efter at programmet er blevet eksekveret til det øjeblik, det interesserer os, er der to muligheder. Du kan erstatte modellen med en ur-for-cyklus-model og fortsætte med udførelsen. Simuleringstilstanden, der bruger eksekverbar kode (det vil sige almindelige kompilerede programfiler) kaldes eksekveringsdrevet simulering. Dette er den mest almindelige simuleringsmulighed. En anden tilgang er også mulig - spor drevet simulering.

Spor-baseret simulering

Den består af to trin. Ved hjælp af en funktionel simulator eller på et rigtigt system samles en log over programhandlinger og skrives til en fil. Denne log kaldes et spor. Afhængigt af hvad der undersøges, kan sporingen omfatte eksekverbare instruktioner, hukommelsesadresser, portnumre og afbrydelsesoplysninger.

Det næste trin er at "afspille" sporet, når ur-for-ur-simulatoren læser sporet og udfører alle instruktionerne skrevet i det. Til sidst får vi eksekveringstiden for dette stykke af programmet, samt forskellige karakteristika ved denne proces, for eksempel procentdelen af ​​hits i cachen.

Et vigtigt træk ved at arbejde med spor er determinisme, det vil sige, ved at køre simuleringen på den ovenfor beskrevne måde, igen og igen, reproducerer vi den samme rækkefølge af handlinger. Dette gør det muligt, ved at ændre modelparametre (cache-, buffer- og køstørrelser) og bruge forskellige interne algoritmer eller tune dem, at studere, hvordan en bestemt parameter påvirker systemets ydeevne, og hvilken mulighed der giver de bedste resultater. Alt dette kan gøres med en prototype enhedsmodel, før der oprettes en egentlig hardwareprototype.

Kompleksiteten af ​​denne tilgang ligger i behovet for først at køre applikationen og indsamle sporet, såvel som den enorme størrelse af sporingsfilen. Fordelene omfatter det faktum, at det er nok kun at simulere den del af enheden eller platformen, der er af interesse, mens simulering ved udførelse normalt kræver en komplet model.

Så i denne artikel så vi på funktionerne i fuld-platform simulering, talte om hastigheden af ​​implementeringer på forskellige niveauer, ur-for-cyklus simulering og spor. I den næste artikel vil jeg beskrive de vigtigste scenarier for brug af simulatorer, både til personlige formål og fra et udviklingssynspunkt i store virksomheder.

Kilde: www.habr.com

Tilføj en kommentar