Linux Kernel – I/O ütemezők tesztje

Régen foglalkoztat már az Linux kernelben lévő I/O ütemezők teljesítménye, de nem igazán ástam bele magamat a dologba. Eddig.

Tulajdonképpen mit is csinál az I/O ütemező?

Az I/O ütemező megpróbálja a beérkező írási/olvasási kéréseket a lehető legoptimálisabban adagolni a lemezvezérlőnek. Hogy a lehető legjobb teljesítményt érhesse el, igyekszik minimalizálni az adatok elérésekor keletkező „seek time” által okozott várakozási időt, priorizálja a I/O műveleteket (pl. a syslog fontosabb, mint mondjuk az Apache access logjai), igyekszik megosztani a rendelkezésre álló sávszélességet a folyamatok között, mindezt úgy, hogy egyik folyamat se érezze magát elhanyagolva és időben megkapja a kért információt. Mint látható, az I/O ütemező egy igen fontos feladatot ellátó programrész a kernelben, így nagyon fontos a célfeladatra optimalizált ütemező használata.

A Linuxban a következő ütemezők használhatóak: noop, anticipatory, deadline és cfq. Így elsőre igazán semmitmondóak a nevek, tehát lássuk, mit is takarnak:

No-Op

A No-op ütemező Jens Axboe nevéhez fűződik. Ez az elérhető ütemezők közül a legegyszerűbb. Nem bűvészkedik különböző elhelyezési vagy anti-defregmentációs algoritmusokkal, egyszerűen csak beérkező sorrendben írja és olvassa az adatokat. Ebből adódóan az optimális felhasználási területe a flash meghajtók kezelése, ahol lényegtelen hova kerülnek az adatok vagy mennyire fragmentált a lemez, hiszen a merevlemezeknél megszokott „seek time” itt nincs jelen, így felesleges erőráfordítás lenne az elhelyezés optimalizálása.

Anticipatory – az előre látó ütemező

Az Anticipatory megpróbálja megtippelni, hogy a következő adat olvasása honnan (és esetleg mit) fog kérni. Így elsőre elég lehetetlennek tűnik a feladat, pedig nem az: statisztikailag az a program, ami adatokat olvasott a lemezről, nagy valószínűséggel a már beolvasott adathalmaz utáni adatokat fogja bekérni a legközelebbi olvasáskor, így szinte csak egy fordulatnyi időt (vagy annyit se) kell várni az adatok megérkezéséig. Optimális esetben.

A hátránya, hogy a tippeléshez kell egy kis idő (pár ms), ami úgymond „kiesik” minden egyes olvasás után, de egy sikeres tipp többszörösen ledolgozza a felhalmozott ms-eket. Bizonyos források szerint Apache webszerverrel akár 71%-os teljesítmény növekedést lehet elérni más I/O ütemezőkkel szemben. Azonban, ahol a tippelés nem működik, 10-15%-kal lassabb, mint a többi megoldás.

Deadline,

azaz határidő. A Deadline sürgősség szerint variálva továbbítja az I/O kéréseket a meghajtók számára. Minden I/O kérésnek van egy kezdete és egy vége. Úgy rendezi sorba a kéréseket, hogy amiknek szorít a határideje, azokat előrepakolja, míg amik ráérnek, azokat késlelteti egy kicsit. Továbbá figyelembe veszi a következő műveletekben felhasznált szektorok helyét is. Ha közeli vagy „útba eső” szektor csoport(ok)ban kell műveleteket végezni, akkor azokat igyekszik egy „seek”-re ütemezni. A PATA szabvány magával hozta az NCQ-t, ami hasonlóan a Deadline-hoz: optimalizálná az adatok beolvasási sorrendjét a gyorsabb elérés érdekében. Gyakorlatilag hibahatáron belül mérhető csak a teljesítménye.

CFQ (Completely Fair Queuing) – Igazságos sorba állítás

A CFQ-t nevezhetnénk az Anticipatory továbbfejlesztésének is. Minden egyes folyamatnak van egy várakozási sora, amiből a kérések a folyamat I/O prioritásának megfelelő sorrendben kerülnek végrehajtásra. Minden egyes olvasási művelet után van egy nagyon kicsi szünet, hátha – az Anticipatory-hoz hasonlóan – pont a szomszédos szektorcsoportból kell adatokat kiolvasni.

Na jó, de melyiket szeressem?

Az ütemezők leírásában megpróbáltam a lényegre hagyatkozni, ami szembetűnően más, mint a többi, így, bár jól elkülöníthetőek az ütemezők, mégsem pontos a kép. Jelenleg a CFQ a Linux alapértelmezett ütemezője. A vélemények megoszlanak arról, hogy ez jó-e vagy sem, mindenesetre leteszteltem mindegyik I/O ütemezőt.

A teszteket tiobench-csel hajtottam végre 1, 2, 4, 8 és 16 szálon. Igyekeztem a lehető legjobb eredményeket produkálni, minimalizálni a hibákat. Minden egyes teszt előtt vártam 2 percet, hogy az ütemezési sor kiürüljön, ürítettem a HDD cache-t is és minden olyan folyamatot leállítottam, ami esetleg beleszólhat az eredménybe.
A rendszer mechanikai egységét egy Seagate SV35.3 családból származó 1 TB-os merevlemez alkotta, Ext4-es fájlrendszerrel.

Pár szó még a tesztekről: Ahol a CPU használata 100%-nál többet mutat, az abból adódik, hogy két processzoros rendszer alatt folyt a teszt. Az egy szálas eredményeknél az átlagos elérési idő nem ütemező, hanem merevlemez limitált. A teszt során 2 GB-os állományokkal dolgoztam, 2.6.32-r7-es félig testre szabott Gentoo kernellel, x86 platformon. A tiobench-ből 0.3.3-r2-es, portage-ben elérhető változatát használtam.

Átviteli sebesség

Processzor terhelés

A tesztekből egyértelműen kiderül, hogy alacsonyabb processzor, de magasabb I/O terhelés mellé érdemesebb az alapértelmezett CFQ ütemezőt használni, mert az I/O szálak növekedésével sem veszt jelentősen az adatátviteli sebességéből, cserébe viszont intenzíven használja a processzort. Fordított helyzetben viszont a Deadline a legjobb megoldás, hiszen van, ahol kevesebb mint a fele CPU terheléssel (és átviteli sebességgel) dolgozik, mint a CFQ vagy az Anticipatory. Köztes megoldásként az Anticipatory lehet a jó választás. Bár jóval nagyobb a processzor igénye, mint a Deadline-nak, mégis még mindig kevesebb, mint a CFQ-nak, ugyanakkor 16 I/O szál esetén is csak kicsit több mint 5 MB/s átviteli különbség van a CFQ javára, de cserébe 31%-kal kevesebb CPU időt igényel.