A sebességmérés programozási vonatkozásairól lesz szó ebben a részben. Mint az élet sok más területén, a benchmark alkotásnál is alapvetően a cél szentesíti az eszközt. Mielőtt belefognánk egy sebességmérő rutin megírásába, néhány felmerülő kérdésre meg kell adnunk a választ:

1) Milyen operációs rendszer alatt, milyen környezetben kell működnie a rutinunknak?
2) Milyen pontosságot várunk el a kapott eredményektől? Mennyi időt szánunk a sebességmérésre?
3) A jövő x86 alapú gépein esetleg előfordulhat-e, hogy a rutinunk hibásan működik? Nem fut vészesen lassan a régebbi gépeken sem?

1. kérdés

A legalapvetőbb problémáról van szó: milyen magasra tesszük a mércét? Hiszen egy DOS alatt futó sebességmérő rutin sokkal szabadabban válogathat a sebességmérő módszerek közül, nem zavarják a multitaszkos operációs rendszerek alatt megszokott párhuzamosan futó alkalmazások, a hardverhez teljes körű hozzáférése lehet. Könnyen belátható tehát, hogy egy benchmark rutint a legkönnyebben DOS alá lehet megírni, a legnehezebben pedig az igazi preemptív multitaszkos operációs rendszerek DOS emulációja alá, ilyen pl. a Windows NT, a Windows 2000, az OS/2 és nem utolsósorban a Linux.

A mércét tehetjük közepes magasságba is, vagyis megelégedhetünk a DOS, Windows 95 és Windows 98 alatti sebességméréssel is, ekkor speciális megszakítási rutinokat hívhatunk segítségül a többi, párhuzamosan futó alkalmazás működésének felfüggesztésére - ezzel a rutinunk a gépben található erőforrások felett egyeduralkodóvá válhat.

Ha már az erőforrásokra terelődött a szó: nagyon fontos jó előre tisztázni, milyen rendszerkövetelmények szükségesek a rutinunk futásához. Milyen processzorra, mennyi memóriára, milyen egyéb perifériákra van szükség a rutin futásához? Aki már egy kicsit programozott assembly nyelven, egyből rávághatja: 'Min. 386-os processzor, 4 MB RAM, VGA kártya - más nem kell'. Ez azonban nem ilyen egyszerű. Pl. az ASMDEMO cache sebesség méréséhez legalább 5. generációs (Pentium osztályú) processzor és 16 MB RAM szükséges. S hogy miért? A későbbiekben fény derül rá...

2. kérdés

Kritikus kérdés: milyen pontos eredményre számítunk? Ha pl. a processzor sebességét MIPS megközelítésben mérjük, nyilvánvalóan pár száz, vagy akár ezer művelet ide vagy oda nem számít. Ilyen esetekben tehát nem is érdemes a '100.1 millió'-nál pontosabb formában megjeleníteni az eredményt. Egyes területeken pedig az eredmények kerekítésére is szükség lehet, pl. a CD sebességének mérésekor elég lehet a '32x' megjelenítése is.

Elsősorban a lefutási idő mérésének módja határozza meg egy rutin eredményének pontosságát. Ha másodpercenként mérjük az időt, nem érdemes az eredmények túl nagy pontosságára számítani, hacsak nem szándékozunk a rutint több percen át futtatni. Ha azonban az időt az 5. generációs processzorokra jellemző TSC (Time Stamp Counter) segítségével mérjük, akkor rendkívüli pontosságot érhetünk el, ezzel pedig nagyon rövid lefutási idejű rutinok írására is lehetőség nyílik.

Ha egy változatos, hosszadalmas sebességmérést végzünk, vagy a vizsgált műveletek egyenként is hosszú ideig tartanak (pl. CD elérési idő vizsgálata), akkor szükség lehet nagyon hosszú lefutási idejű rutinra. A várakozás azonban a felhasználó idegeit alaposan felőrli, így túlzott mértékű használata nem ajánlott. Ilyen esetkben érdemes többfajta megoldást ötvözni: lefuttatunk egy rövid rutint, majd az eredmény pontosságától függően eldönthetjük, szükség van-e a hosszadalmas műveletsorozatra. Így működik az ASMDEMO video sebesség mérése is: első lépésben fél másodpercig próbál minél több képkockát kirakni, majd ha a megjelenített képkockák száma meghaladja a 2-t, nem is megy tovább a régi, 4 másodpercig tartó rutinra. Ha azonban régi, lassú videokártyát tesztel, akkor a rövid rutin pontossága alacsony lesz, és automatikusan lefut a hosszadalmas rutin...

3. kérdés

Nagyon sok régebbi programban találhatunk olyan sebességmérő rutinokat, melyek nem működnek a mai 6. generációs processzorokon. Nem is csoda, hiszen ha egy rutint úgy írunk meg, hogy nem tervezünk jó előre, előfordulhat, hogy akár már 1 év múlva sem fog az akkori modern gépeken futni az egyébként jól kitalált, és nem kevésbé jól megvalósított benchmark tesztünk.

Nem elegendő, ha csak és kizárólag a nyers számítási sebesség növekedésére számítunk. A processzorok teljesítménye kb. 16-18 havonta duplázódik, ezzel mindenképpen számolnunk kell, ha sebességet akarunk mérni. Ezen kívül azonban a cache méretének folyamatos növekedése, a párhuzamosítás tökéletesedése, új művelet-végrehajtási mechanizmusok, trükkök megjelenése várható.

Mindent összevetve, nem egyszerű feladat benchmark rutint írni, nem árt jó előre átgondolni, vajon érdemes-e egy ekkora kalandba belevágni (a válasz: IGEN! :)

Módszerek

Tekintsük át (a teljesség igénye nélkül), milyen módszerek kínálkoznak a sebesség-, pontosabban a készülő rutinunk végrehajtási idejének mérésére:

  • eltelt másodpercek mérése a DOS vagy a BIOS órája segítségével 0.055 (1 / 18.2) másodperces pontosságú időmérés
  • a BIOS számlálója segítségével 0.00000084 (1 / 1193000) másodperces pontosság elérése
  • a PIT (Programmable Interval Timer) használatával 0.000000002 (1 / 500000000) másodperces pontosság (500 MHz-en)
  • a TSC (Time Stamp Counter) használatával
Talán hihetetlennek tűnik, de nagyon sok esetben egyedül csak a 4. módszer használható!

Lássuk az egyes módszerek előnyeit és hátrányait, az alkalmazási lehetőségeket és azok buktatóit...

Másodpercek mérése

A legegyszerűbben és legszélesebb körben alkalmazható időmérési módszer. Előnye: minden DOS kompatíbilis környezetben alkalmazható, nem követeli meg a hardver közvetlen elérését, valamint multitaszkos operációs rendszerek alatt is működik. Remekül használható hosszas műveletekhez (pl. CD vagy DVD sebesség mérése), de a másodperces pontosság hátránya, hogy igazán precíz eredményt nem kaphatunk.

Minden sebességmérő módszert kétféleképpen alkalmazhatunk. Az egyik verzió: lekérdezzük a időméréshez használt számláló (jelen esetben a gép beépített órájának) állását, lefuttatjuk a rutinunkat, újból lekérdezzük a számlálót, kivonjuk a számláló két állását egymásból, ezzel megkapjuk az eltelt időt. Ez a módszer a legtöbb esetben nem állja meg a helyét, hiszen a különböző sebességű gépeknél a rutin végrehajtási ideje szélsőséges értékek között mozoghat. Lássunk egy egyszerű példát: mérjük le a merevlemez olvasási sebességét oly módon, hogy leolvasunk a vinyó elejéről 10 MB-ot, lemérjük ennek idejét (másodpercben), majd a 10-et a kapott értékkel elosztva (10 / eltelt idő) megkapjuk a merevlemez olvasási sebességét MB/s-ban. Ez nem is lenne rossz módszer, egészen addig, amíg a merevlemez sebessége 1 és 10 MB/s között mozog. Ha azonban a sebesség vészesen alacsony - pl. öreg MFM-es daraboknál akár 200 KB/s is lehet -, akkor a rutin lefutási ideje akár 1 perc is lehet! A másik véglet: a mai modern winchesterek (pl. a közkedvelt Quantum Fireball EX és CR sorozat) sebessége 12-14 MB/s is lehet, ezeknél tehát szélsőséges esetben 0 másodperc alatt megtörténhet a 10 MB leolvasása, így ha nem vigyázunk, a programunk könnyen elszállhat 0-val való osztással (10 / 0 másodperc)...

Van azonban egy másik módszer, mely mentes az efféle buktatóktól. Maradjunk az előbbi, winchester olvasási teszt példánál. Kérdezzük le az időt, futtassunk le egy rövidebb rutint (pl. 64 KB olvasása), nézzük meg, eltelt-e már 1 másodperc. Ha nem, ismételjük a műveletet egészen addig, amíg a másodperc számláló vált. Ezzel egy másodperc alatt annyit olvastunk a winchesterről, amennyit az átviteli sebesség megenged. A biztonság kedvéért futtasuk le párszor ezt a műveletsort, mondjuk háromszor. Így 3 másodpercig folyamatosan olvasunk a merevlemezről, és számoljuk, hány 64 KB-os blokkot sikerült a rendszernek átvinnie. A végén a blokkok számát elosztjuk 16-tal (hogy MB / 3 s-ot kapjunk), és még 3-mal, hogy a végleges MB/s előálljon. Ennek a módszernek az a nagy előnye, hogy egy vén MFM-es vinyón és egy modern UDMA/66-os Quantumon is 3 másodpercig fut, valamint nem kell a 'túl gyors' winchestereknél nullával való osztástól félnünk.

Összefoglalva: a 3 felmerült kérdés mindegyikére kedvező választ kaphatunk, ha másodpercenként mérünk, és a második felvázolt módszer szerint járunk el. A pontosság mértéke csakis attól függ, időben milyen hosszan hajtjuk végre a műveleteket.

55 ezredmásodperces pontosság

A BIOS INT 8-as megszakítása másodpercenként kb. 18.2-szer növeli a memória 0040h:006Ch címén található duplaszó (Pascal nyelven: longint) értékét. Ez a BIOS számlálója, melynek használata hasonló, mint a másodpercek mérése, azaz ez is alkalmazható minden DOS kompatíbilis környezetben, ez sem követeli meg a hardver közvetlen elérését, valamint multitaszkos operációs rendszerek alatt is ugyanúgy működik, mint a másodpercek mérése. További előnye, hogy kb. 18-szor nagyobb pontosságra képes, ezért nemcsak a perifériák, hanem akár a processzor vagy a memória sebességének mérésére is használható.

Az egyetlen buktató: a számláló éjfélkor lenullázódik, így ha a sebsségmérés ebben a szerencsétlen időpontban is történhet, akkor erre az apróságra fokozottan ügyelni kell. Minden más tekintetben az előző módszernél leírtak az érvényesek, azaz itt sem érdemes egy jól meghatározott műveletsorozat lefutási idejét mérni, mert könnyen előfordulhat túl- vagy alulcsordulás.

Vannak azonban olyan esetek, amikor mégis célszerűbb a rosszabb módszert használni. Ilyen lehet pl. a MIPS alapú sebességmérés. Az ASMDEMO MIPS16 rutinja egy rövid, mindössze 94 utasításból álló ciklusmagot iterál 1 milliószor, és ennek méri a lefutási idejét. A lefutási idő egy i486DX2/66-os CPU esetén 52 egység (52 / 18.2 = 2.86 másodperc), míg egy K6-2/450 számára mindössze 4 időegység (4 / 18.2 = 0.22 s) elegendő az 1 millió iteráció végrehajtásához! Látható, hogy egy fiktív K6-2/1000 processzor teljesítménye annyira nagy lenne, hogy a MIPS16 végrehajtása kevesebb, mint 2 időegységet venne igénybe. Ez azt jelentené, hogy a számláló mindössze 1-gyel növekedne (mivel 2 egységnyi idő nem telik el, csak valamivel kevesebb), ezzel pedig a MIPS16 eredménye 1710.8 lenne. Ez nem felelne meg a valóságnak, mivel a K6-2/450 ASMDEMO MIPS16 szerinti sebessége 420.2, így a K6-2/1000 sebessége valahol 934 körül kellene, hogy mozogjon...

A probléma megoldása rendkívül egyszerű: ha túl gyorsan lefut a rutinunk, futtassuk le újra, de több iterációval. Az ASMDEMO MIPS16 rutinja először 1 millió iterációval fut le, majd ha a MIPS16 eredmény 60-nál magasabb, akkor a sebességmérő rutin újra lefut [eredmény / 30] millió iterációval. Ezzel biztosítható a helyes, pontos működés egészen 1710.8 MIPS16-nak megfelelő sebességű processzorig (kb. K6-2/1800). Az ASMDEMO azonban tovább tökéletesíti a módszert, és az ennél is gyorsabb processzoroknál, vagyis ahol az 1 millió iteráció lefutási ideje 0 időegység (ezáltal lenne a MIPS16 nagyobb, mint 1710.8), nos ilyen esetekben az iterációk számát addig duplázza a rutin, amíg a lefutási idő 0-nál nagyobb lesz. Az így átalakított MIPS16, MIPS32, FPU24 stb. rutinok szimulációs körülmények között képesek voltak egy K6-2/3000 processzor teljesítményének precíz mérésére is!

PIT

A PIT használata olyan esetekben ajánlott, amikor rendkívüli pontosságra van szükség, és a processzor nem támogatja a TSC-t. Sajnos a PIT a hardver közvetlen elérésével programozható csak fel, így használatáról le kell mondanunk minden multitaszkos környezetben. Csak DOS alatt alkalmazható, de ott minden más módszernél hatékonyabban működik. Alkalmas a processzor és a memória sebességének mérésére, előszeretettel használják a CPU és az FPU órajelének megállapítására.

A PIT felprogramozása nem bonyolult művelet, mindössze két néhány soros rutinra lesz szükségünk. A sebességmérő rutinnal azonban csínján kell bánni, hiszen a PIT számlálója másodpercenként 18.2-szer átfordul, vagyis a rutinunknak igyekeznie kell, hogy minden processzoron 0.055 másodperc alatt lefusson. Ha azonban ez mégsem megoldható, akkor sem kell kétségbe esnünk, hiszen az előző témakörben tárgyalt BIOS számláló pont ilyen időközönként növekszik 1-gyel! Ennek a magyarázata rendkívül egyszerű: a PIT számlálója 65535-ről indul, és másodpercenként 1193000-szer (1.193 MHz) csökken eggyel. Amikor a PIT számláló eléri a 0-t (másodpercenként 18.2-szer történik ilyen), ez kivált egy INT 8 megszakítást, mely a BIOS számlálóját növeli 1-gyel. A PIT és a BIOS számlálója tehát szoros kapcsolatban van egymással, a BIOS számlálója a PIT-hez viszonyítva pontosan 65536-od pontosságú.

Tehát, ha a rutin nem fut le 0.055 másodperc alatt, akkor nincs más dolgunk, mint a BIOS számlálóját figyeljük, és a BIOS váltások számát felszorozzuk 65536-tal, ehhez pedig hozzáadjuk a PIT számláló által mért időt.

A CPU órajelének mérése jó példa az előző részben tárgyalt spekulatív végrehajtás buktatóinak kiküszöbölésére. A modern processzorok képesek automatikusan, a program futtatása közben párhuzamosítani a kódot. Ez azonban nem minden műveletre érvényes, vannak ugyanis olyan komplex utasítások, melyek abszolút nem párhuzamosíthatóak. Ilyen pl. az XCHG AX,BX assembly utasítás. Nem szükséges azonban ilyen utasítás használata, ha ugyanis tisztában vagyunk a processzor képességeivel, elegendő lehet egy szimpla AAM is, hogy az órajelet mérni tudjuk. Sokszor végrehajtjuk egymás után ugyanazt a kiválasztott utasítást, lemérjük a végrehajtási időt, és ebből kiszámítunk egy konstansot, mely az adott processzorra jellemző. Ebből a konstansból pedig bármely azonos típusú (általában azonos processzormaggal rendelkező) CPU órajele kiszámítható. Az ASMDEMO e célra íródott rutinja az AAM utasítást használja, melyből 2046-ot hajt végre, ennek lefutási idejét pedig a PIT-tel méri. Szimpla DOS alatt (ha a CPU támogatja) a TSC-t használja, multitaszkos környezetben pedig a BIOS számlálóját hívja segítségül. Látható tehát, hogy egy igazi, mindenhol alkalmazható sebességmérő rutinnak a siker érdekében gyakran többféle módszert is alkalmaznia kell...

TSC

A P5, vagyis a legelső Pentium processzorban jelent meg először. A TSC nem más, mint egy olyan számláló, mely a processzor minden órajelütése alkalmával növekszik 1-gyel. Ennél pontosabban nem lehet mérni az időt, tehát a TSC használatával minden létező feladat megoldható. Alkalmazásához azonban szükség van egy, a TSC-t támogató processzorra, ez a mai világban már nem probléma, hiszen a túlnyomó többség már ilyen, vagyis legalább 5. generációs processzorokat használ. A TSC használata azonban csak DOS alatt egyszerű, multitaszkos környezetben csak 32 bites védett módú programok használhatják. Ha tehát egy valós módú, DOS alapú, mondjuk Pascalban íródott programban szeretnénk a TSC-t használni, akkor le kell mondanunk a Windows, OS/2 és Linux alatti futásról, hacsak nem veszünk elő valami trükköt. Pl. írhatunk egy külső, 32 bites védett módú modult (pl. DOS32, PMODE vagy Watcom C/C++ segítségével), melyet az Exec paranccsal bármikor futtathatunk, s csak a mérési paraméterek, majd az eredmények átadását kell megoldanunk...

Van még egy apró probléma a TSC-vel: mivel nagyon nagy pontosságú számláló, ezért a szélessége 64 bit, melyet egy 16 bites programban nehézkesen tudunk csak kezelni. A feladat azonban nem lehetetlen, a következő részben erre is láthatunk példát!
 

... to be continued ...

A 3. részben végre áttérünk a gyakorlat megvalósításra, mely az elméleti alapozás után remélhetőleg már nem igényel túl sok magyarázatot...