Eötvös Loránd Tudományegyetem Informatikai Kar
Adateléréssel kapcsolatos optimalizációt támogató szoftver Szakdolgozat
Palmer Márk Programtervező informatikus (Bsc) Szoftverfejlesztő informatikus (B) szakirány Témavezető: Dévai Gergely Programozási Nyelvek és Fordítóprogramok Tanszék
Budapest, 2015
Tartalomjegyzék 1 Bevezetés...........................................................................................................................................3 1.1 A szakdolgozat leírása................................................................................................................3 1.1.1 A feladat leírása..................................................................................................................4 1.2 Témaválasztás oka.....................................................................................................................5 2 Felhasználói dokumentáció...............................................................................................................6 2.1 A feladat rövid ismertetése.........................................................................................................6 2.2 Célközönség...............................................................................................................................6 2.3 Telepítési útmutató.....................................................................................................................6 2.4 Adatok bevitele..........................................................................................................................7 2.4.1 Űrlap kitöltése....................................................................................................................8 2.4.2 Az Űrlap visszajelzései......................................................................................................9 2.4.3 XML fájl feltöltése.............................................................................................................9 2.4.4 A program visszajelzései..................................................................................................10 2.5 A program működése...............................................................................................................10 2.6 Mentési lehetőségek.................................................................................................................12 2.6.1 Konfiguráció mentése......................................................................................................12 2.6.2 Konfiguráció mentése optimális megoldással..................................................................12 3 Fejlesztői dokumentáció..................................................................................................................13 3.1 Megvalósítási terv....................................................................................................................13 3.2 A fejlesztéshez felhasznált eszközök.......................................................................................15 3.3 Az MVC architektúrális minta.................................................................................................15 3.3.1 Modell..............................................................................................................................16 3.3.2 Nézet................................................................................................................................16 3.3.3 Vezérlő..............................................................................................................................16 3.3.4 Áttekintés.........................................................................................................................17 3.3.5 Az MVC megvalósítása PHP-ban....................................................................................17 3.3.6 Az MVC megvalósítása CodeIgniterben..........................................................................18 3.4 A megvalósítás részletei...........................................................................................................18 3.4.1 A kiszolgáló oldali implementáció...................................................................................18 3.4.2 A kliens oldali implementáció..........................................................................................25 3.4.3 Az optimalizáló eljárás implementációja.........................................................................26 3.4.4 A felhasználói felület........................................................................................................29 3.4.5 1140.css............................................................................................................................31 3.4.6 Kommunikáció a kiszolgáló és kliens között...................................................................32 3.5 Tesztelés...................................................................................................................................32 3.5.1 Felhasználói esetek tesztelése..........................................................................................32 3.5.2 A főoldal eseményei.........................................................................................................32 3.5.3 XML fájl feltöltés események..........................................................................................33 3.5.4 Űrlap események..............................................................................................................34 3.5.5 A program felületének eseményei....................................................................................36 3.5.6 A tesztelés tanulságai.......................................................................................................39 3.5.7 Egységtesztelés................................................................................................................39 3.5.8 Az XML_Model osztály egységtesztelése.......................................................................42 3.5.9 A tesztesetek felülete........................................................................................................44 3.5.10 A tesztelésből levont tanulság........................................................................................44 3.6 Hatékonyság vizsgálat.............................................................................................................45 4 Hivatkozások...................................................................................................................................48 5 Irodalomjegyzék..............................................................................................................................49
1 Bevezetés 1.1 A szakdolgozat leírása Manapság a számítógépek gyorsaságát nem kizárólag a processzor teljesítménye határozza meg. Fontos szerepet játszik a számítógép összesített teljesítményében a különböző memória szintek mérete és gyorsasága. Egy memóriaszint gyorsasága alatt azt értjük, hogy a processzor mennyi idő alatt fér hozzá a memóriaszinten
tárolt
egységnyi
információhoz.
A
memóriaszintek
hierarchiában
helyezkednek el, ebben elfoglalt helyüket az elérési idejük határozza meg.[1]
A memóriaszintek hierarchiája (forrás: http://cse1.net/) A mai processzorok többségének háromszintű gyorsítótára van. Az általában L1-el (Level 1) jelölt memóriaszint a leggyorsabb, de ennek a legkisebb a tárolókapacitása. Az L3-al jelölt hármas szint ellenben lassabb, de a kapacitása az első szint többszöröse, míg az L2-vel jelölt köztes szint elérési ideje és kapacitása is az L1 és L3 szint közé esik. Még ezeknél is lassabb 3
az operatív tároló (RAM), amely manapság 4-8 GB tárolókapacitással rendelkezik asztali gépek és laptopok esetén. A leggyorsabban hozzáférhető adatok a processzor regisztereiben vannak. Mivel a regiszterekből nagyon kevés van a számítógép teljes memóriakapacitásához mérten, és a processzor csak átmeneti tárolóként használja őket, ezért ezekkel a szakdolgozatomban nem foglalkozom. A leglassabb memóriaszint általában a háttértár. Optimális esetben a programok által felhasznált adatokat nem itt tárolja a számítógép, ezért ez kívül is esik a szakdolgozatom témáján. A fentebb leírt felépítésnek az az oka, hogy a gyorsabb memóriáknak (SRAM – statikus RAM[2], a processzor regisztereihez és gyorsítótárához használt technológia) lényegesen költségesebb a gyártásuk, mint a lassabb operatív táraknak (DRAM – dinamikus RAM [3]). Így értelemszerűen SRAM-ból jóval kevesebb van egy számítógépben, mint DRAM-ból. A számítógép működése során arra kell törekedni, hogy a programok által használt adatok mindig a lehető leghatékonyabban helyezkedjenek el a memóriaszinteken. Minél többször fér hozzá a processzor egy adott adathalmazhoz, annak annál gyorsabb memóriaszinten kell elhelyezkednie. Ennek megfelelően a kevésbé használt adatok a lassabban hozzáférhető memóriaszinteken helyezkednek el. Egyes architektúrákon a processzor dönt arról, hogy mely adathalmazokat gyorsítótárazza, így a szoftverfejlesztőknek nincs beleszólásuk ebbe a folyamatba. De vannak olyan architektúrák is, ahol a programozó dönthet erről. Szakdolgozatom célja ez utóbbi architektúrák esetén az optimális allokáció meghatározása.
1.1.1 A feladat leírása Feladatom adott memóriaszintek (név, méret, hozzáférési idő) és adott objektumok (név, méret, számosság, hozzáférések száma) esetén a lehető leghatékonyabb elrendezés (konfiguráció) meghatározása és a felhasználó által megadott konfiguráció hatékonyságának értékelése. Egy objektum egy memóriaszinthez tartozhat (ahol az eltárolódik), ha a memóriaszinten van elég szabad hely az objektum tárolásához. 4
A felhasználó drag-n-drop (fogd és vidd[4]) módszerrel adhatja meg a saját konfigurációját. Amennyiben a felhasználó elhelyezett minden objektumot, a program kiszámítja a konfiguráció hatékonyságát. Ez utóbbi lehetőség csak akkor elérhető, ha a program már kiszámította a leghatékonyabb megoldást, hisz ehhez mérten határozzuk meg a felhasználó megoldásának hatékonyságát. A programnak egy űrlap formájában lehetőséget kell biztosítania arra, hogy a felhasználó felvigye az adatokat. Alternatív lehetőségként szöveges fájl formájában is megadhatóak a bemenő adatok. Miután betöltött a program, lehetőség adódik a munka mentésére, ebben az esetben a program egy szöveges fájlba menti a bemenetei adatokat, és a felhasználó által megadott elrendezést is. Ha a program meghatározta a leghatékonyabb megoldást, az is elmenthető. Utóbbi lehetőségre azért van szükség, mert bizonyos esetekben nagyon lassú az optimalizálási folyamat. A programnak az adatok felvitele után el kell kezdenie a feladat optimalizálását (amennyiben ez még nem történt meg). Ezalatt a felhasználó már elkezdheti a munkáját és drag-n-drop módszerrel elhelyezheti az objektumokat a memóriaszinteken. A programnak tájékoztatnia kell a felhasználót az optimalizálás előrehaladottságáról.
1.2 Témaválasztás oka Azért választottam ezt a témát, mert mindig is érdekeltek a fordítóprogramok és az optimalizációkkal kapcsolatos problémák.
5
2 Felhasználói dokumentáció A programot egy weboldal formájában implementálom, így a program a megfelelő webcím böngészőbe írása után érhető el.
2.1 A feladat rövid ismertetése Adott n darab objektum és m darab memóriaszint és az ezekhez tartozó adatok (objektumok esetén név, példányszám, egységnyi méret és elérések száma, memóriaszintek esetén pedig név, kapacitás, elérési sebesség). Az objektumok elhelyezhetőek a memóriaszinteken, amennyiben az adott szinten van elég szabad hely a tárolásukhoz. Egy objektumot csak egy memóriaszinten helyezhetünk el. Így meghatározható egy elrendezés (konfiguráció). A feladatom a lehető leghatékonyabb konfiguráció meghatározása (ha van ilyen) és a felhasználó által megadott elrendezés hatékonyságának kiszámítása.
2.2 Célközönség Szakdolgozatomat legfőképp programozók használhatják fel a munkájukhoz, akik olyan architektúrán dolgoznak, ahol van lehetőségük meghatározni, hogy a processzor hogyan kezelje a gyorsítótárat.
2.3 Telepítési útmutató A szoftvert csupán egy kiszolgáló számítógépre kell telepíteni, amely egy PHP-t is futtatni képes webszervert üzemeltet. Ha ez már rendelkezésre áll, akkor több kliens használhatja a programot akár egy időben is. Amennyiben adott a megfelelő szerver számítógép, a telepítés csupán annyiból áll, hogy a programot átmásoljuk a webszerver gyökérkönyvtárába (Apache esetén a httpd.conf konfigurációs fájlban a ServerRoot név alatt találjuk a mappa elérési útvonalát). Követelmények: •
PHP futtatási lehetőség (5.1.6 vagy frissebb verzió).
6
•
.htaccess használati lehetőség (a legtöbb ingyenes tárhely szolgáltató ezt nem engedélyezi).
2.4 Adatok bevitele Az adatok bevitele kétféleképpen lehetséges. Az egyik módszer az űrlap kitöltése, a másik esetben fájlfeltöltéssel vihetőek fel az adatok egy XML fájl [5] formájában. Az utóbbi módszer akkor lehet igazán hasznos, ha az adatokat egy másik program generálja és XML fájlban tárolja el úgy, ahogyan ez a program be tudja olvasni. Az adatbevitel módszerét a bal oldali menüben választhatjuk ki a megfelelő gomb megnyomásával. Ez a menü mindig elérhető, így bármikor új projektet kezdhetünk valamelyik gomb megnyomásával. Legalább egy-egy memóriaszint és objektum szükséges a projekt kezdéséhez, amennyiben a felhasználó által felvinni kívánt adatok ennek a követelménynek nem felelnek meg, a program tájékoztatja a felhasználót. Memóriaszintenként a következő adatokat kell megadni: •
Név (Szöveg)
•
Méret (Egész szám)
•
Elérési sebesség (Egész szám)
Objektumonként a következő adatokat kell megadni: •
Név (Szöveg)
•
Méret (Egész szám, ez az érték egy objektum méretét jelöli)
•
Számosság (Egész szám, a memóriában található ilyen típusú objektumok száma).
•
Elérések száma (Egész szám, azt jelöli, hogy egy objektumhoz egységnyi idő alatt hányszor fér hozzá a processzor)
A név az egyetlen opcionális érték, a többit kötelező megadni. A méret kitöltésénél űrlap esetén megadható mértékegység, XML fájl esetén a definiált érték byte-ban értendő.
7
Új projekt kezdése
2.4.1 Űrlap kitöltése Az űrlap kitöltése gombra kattintva egy űrlapot kapunk, amelyen megadhatjuk a memóriaszintjeink és objektumaink adatait.
Űrlap kitöltése Az alap állapot az, hogy egy-egy memóriaszintet és objektumot adhatunk meg. Ezeket természetesen kiegészíthetjük többel is tetszés szerint. Az Új objektum gombra kattintva egy új sornyi adatmezőt kapunk ahol megadhatunk egy újabb objektumot. Az Új memóriaszint gomb ugyanezen elv alapján működik. Amennyiben egy memóriaszintre vagy objektumra még sincs szükségünk, a Legutolsó törlése gombbal az utolsó sornyi adatmezőket eltávolíthatjuk.
8
Ha az adatok kitöltésével kész vagyunk, a Küldés gombra kattintva kezdhetjük el a projektet. Ekkor megjelenik a program felülete, ahol már el is kezdhetjük a szerkesztést.
2.4.2 Az Űrlap visszajelzései A program nem engedi elküldeni a hibásan kitöltött űrlapokat. Ha a felhasználó rákattint egy mezőre és oda nem megfelelő értéket ír és átkattint egy másikba (vagy üres helyre kattint), akkor a program azonnal figyelmezteti.
Hibásan kitöltött űrlap
2.4.3 XML fájl feltöltése Az adatok felvihetőek XML fájl formájában is. Ezt a funkciót akkor célszerű használni, ha egy másik program kimenetét szeretnénk felhasználni. XML fájl manuális szerkesztése helyett inkább töltsük ki az űrlapot az előbb említett felületen és mentsük el a projektet. Ennek eredményeként megkapjuk az űrlapon kitöltött adatokat XML formában, amelyet később is felhasználhatunk.
9
XML fájlok feltöltésére szolgáló felület A program csak szabványos XML fájlokat fogad el. [6] Ezen belül is csak azokat, amelyek a következő formájúak: <project> <memory>
L2 <size>524288 <speed>2 …
Objektum #1 <size>7 10 120300 … A feltöltött fájl kiterjesztését a program nem veszi figyelembe. A név megadása ez esetben is opcionális.
2.4.4 A program visszajelzései Ebben az esetben a program nem tud azonnal reagálni a nem megfelelően kitöltött XML fájlokra. Az elküldés után azonban ellenőrzi, hogy a fájl szabványos XML szöveget tartalmaze. Ha igen, akkor módszeresen átvizsgálja a fájl tartalmát, hogy abban megtalálható-e minden szükséges adat és hogy azok megfelelő formátumúak-e.
Nem szabványos XML fájl feltöltése esetén ezt a hibaüzenetet kapjuk
2.5 A program működése Miután sikeresen megadtuk a programnak a használni kívánt adatokat, a program felülete 10
jelenik meg.
A program felülete A program a felület betöltése után megkezdi a feladat optimalizálását. A folyamat előrehaladottságáról a program tájékoztatja a felhasználót. Ekkor már elkezdhetjük a szerkesztést. A weboldal legalján találhatóak az objektumok, ezeket drag-n-drop módszerrel megfoghatjuk és letehetjük azon a memóriaszinten, amelyen az adott objektumot el szeretnénk helyezni. Figyeljünk rá, hogy a megfelelő helyre tegyük az objektumainkat. Ha nem egy memóriaszint felett (a világoskék felületen) engedjük el az objektumot, akkor az visszakerül eredeti helyére. Ugyanez történik akkor is, ha az objektumot olyan memóriaszintre akarjuk elhelyezni, ahol már nincs elég hely annak eltárolásához.
Szerkesztés drag-n-drop módszerrel Amennyiben minden objektumot elhelyeztünk a kívánt memóriaszinten és a program befejezte a feladat optimalizálását, megkapjuk az általunk megadott konfiguráció
11
hatékonyságát százalékban. A 100% a lehető leghatékonyabb megoldást jelöli, a 0% pedig a lehető legrosszabb konfiguráció értéke.
2.6 Mentési lehetőségek Ha befejeztük munkánkat, lehetőségünk van azt elmenteni a program felületén található Mentés és Mentés optimális megoldással gombra kattintva. Utóbbi lehetőség csak akkor érhető el, ha a program befejezte az optimalizálást. Az elmentett konfigurációkat az XML fájl feltöltése gombra kattintva tölthetjük be. Ekkor ott folytathatjuk a munkánknak, ahol azt abbahagytuk.
2.6.1 Konfiguráció mentése A Mentés gombra kattintva a böngésző automatikusan elkezdi letölteni a program által generált XML fájlt. A letöltött XML fájl felépítése azonos a fent említettekkel, azzal a különbséggel, hogy a program elmenti az objektumok helyzetét.
2.6.2 Konfiguráció mentése optimális megoldással A program lehetőséget ad a konfigurációnk elmentésére a feladathoz tartozó optimális megoldással együtt. Ez a lehetőség akkor hasznos, ha a feladat optimalizálása nagyon lassú.
12
3 Fejlesztői dokumentáció A programot egy weboldal formájában valósítjuk meg. A kiszolgáló oldalon PHP-t, a kliens oldalon pedig JavaScript-et használva. Az optimalizáló eljárást JavaScript nyelven valósítottam meg. Ez nem a leghatékonyabb megoldás, mert a böngészők általában egy-egy rendszerfolyamatként kezelik a megnyitott lapokat és a JavaScript nyelv nem biztosít eszközöket a párhuzamos programozáshoz. Tovább rontja a helyzetet, hogy a JavaScript interpretált nyelv (script nyelv), így az ezen a nyelven írt programok lassabban futnak, mint a gépi kód. A fejlesztőeszközök kiválasztása során fontosabb szempont volt számomra az egyszerű elérhetőség. Egy weboldal formájában megvalósított programot könnyen elérhetünk bárhonnan, csak egy számítógépre, egy azon futó böngészőre és egy internetkapcsolatra van szükségünk. A szakdolgozatom célja a feladat és annak megoldásának bemutatása. Egy élesben is használt, nem demonstráló jellegű optimalizáló eljáráshoz természetesen egy hatékonyabb, gépi kódra fordítható programozási nyelvet használtam volna párhuzamosítással.
3.1 Megvalósítási terv Ahogy azt már korábban (a felhasználói dokumentációban) leírtam, a felhasználó a főoldal meglátogatása után két opció közül választhat: a beviteli adatokat XML fájl formájában vagy egy űrlapon adja meg. Ez diagramként így rajzolható meg:
A főoldal felhasználói esetei 13
A fenti menü gombjait nem tekintem külön felhasználói eseteknek, mivel ezek csak információkat adnak a felhasználónak a programról. Az optimális megoldással rendelkező XML fájlokat pedig ugyanazon a felületen lehet feltölteni, mint azokat amelyekbe nincs elmentve az optimális megoldás, ezért ez egy felhasználói esetnek minősül. Miután a felhasználó megadta a beviteli adatokat, a program betöltődik. Itt több lehetőség is adódik: rögtön elkezdheti a szerkesztést, elmentheti a konfigurációt vagy kiléphet (ilyen opciót a program maga nem biztosít, ezalatt a böngészőprogram bezárását vagy az oldal elhagyását értem). Alternatív lehetőségként a felhasználó megvárhatja, hogy az optimalizáló eljárás befejezze a munkáját, ekkor két további lehetőség áll a rendelkezésére: megtekintheti az optimális megoldást, vagy elmentheti a jelenlegi konfigurációt az optimális megoldással együtt. A programhoz tartozó felhasználói esetek:
A program által felkínált lehetőségek.
14
3.2 A fejlesztéshez felhasznált eszközök A fejlesztést Windows 8.1 operációs rendszeren végeztem a következő eszközök használatával: •
Notepad++
•
XAMPP for Windows 3.2.1
•
•
Apache verzió: 2.4.10
•
PHP verzió: 5.5.15
Google Chrome
A Notepad++ helyettesíthető Netbeans fejlesztőkörnyezettel, a Google Chrome bármilyen más böngészővel (ajánlott a legfrissebb verziót használni az esetleges stíluslap vagy szkript hibák elkerülése végett), a XAMPP pedig ugyanilyen, vagy frissebb verziójú Apache és PHPval. A fejlesztés során felhasznált keretrendszerek és könyvtárak: Kiszolgáló oldalon: •
CodeIgniter Framework 2.2.0[7]
Kliens oldalon: •
jQuery 1.11.1[8]
•
jQuery UI 1.11.2[9]
Azért választottam ezeket az eszközöket, mert ezekkel viszonylag (más eszközökhöz mérten) gyorsan le tudtam fejleszteni a programot. A CodeIgniter-t az egyszerű MVC keretrendszer[10] megvalósítása miatt, a jQuery-t a JavaScript kód leegyszerűsítése miatt, a jQuery UI-t pedig a drag-n-drop módszer egyszerű implementálhatósága miatt választottam.
3.3 Az MVC architektúrális minta A weboldalak fejlesztésénél leggyakrabban használt architektúrális minta az MVC (Model View Controller). A minta három részre bontja a weboldalakat: model (modell), view (nézet) 15
és controller (vezérlő).
3.3.1 Modell A modell feladata az adatok kezelése. Az alkalmazás futása során a modell tartja a kapcsolatot a külső adatforrásokkal, melyek lehetnek adatbázisok vagy fájlok. A modell betölti a szükséges adatokat a fájlokból és/vagy adatbázisokból, szükség esetén pedig adatokat visz fel az adatbázisba vagy ír fájlba. A modell csak a vezérlővel tartja a kapcsolatot és csak a vezérlő kérésére hajt végre feladatot. A modell adott esetben a reprezentálandó adatokhoz típusokat is biztosít és ezekkel dolgozik. Ennek megfelelően a modell lehet egyetlen objektum, amely adatok mozgatásával foglalkozik, vagy akár egy több objektumból álló struktúra amely a bonyolultabb szerkezetű adatok kezelését segíti elő.[11]
3.3.2 Nézet A nézet a megjelenítésért felelős. A vezérlőtől kapja az adatokat, amelyeket a felületén megjelenít. A vezérlő dönti el, hogy melyik nézetet akarja betölteni és a vezérlő adja át az adatokat a betöltendő nézetnek. A nézet feladata továbbá az is, hogy a felhasználó által kiváltott eseményeket továbbítsa a vezérlő felé. A vezérlő ekkor elvégzi a szükséges módosításokat (amennyiben ez szükséges) és visszajelez a nézetnek, amely megváltoztatja állapotát. A nézet nem áll közvetlen kapcsolatban a modellel, a nézetnek és a modellnek egyaránt csak a vezérlővel szabad kommunikálnia. Ha a nézet közvetlenül hivatkozik a modellre vagy fordítva, az az újrafelhasználhatóság rovására mehet, ez pedig az MVC céljaival ellentétes.[12]
3.3.3 Vezérlő A vezérlő az egész folyamatot irányítja. Kapcsolatban áll a modellel és a nézettel, a modelltől adatokat kérdez le, amelyeket aztán a nézetnek átadva megjelenít. Általánosságban minden olyan kód, amely nem a megjelenítéssel vagy adatkezeléssel foglalkozik a vezérlőbe kerül. Ezt nevezzük üzleti logikának. Törekedni kell a vezérlőben lévő kód minimalizálására a lehetőségekhez mérten.
16
3.3.4 Áttekintés A MVC minta biztosítja a webalkalmazások egyszerű és áttekinthető felépítését. A minta leginkább a következő képpel szemléltethető.
MVC minta (kép forrása: http://stackoverflow.com) Az MVC nem egy tervezési minta. Nincs megszabva, hogy pontosan hogyan kell megvalósítani. Mivel minden programozó számára mást jelent ez a fogalom, ezért leginkább fejlesztő- és nyelvspecifikus a megvalósítása. Az MVC minta Tim Berners Lee-től származik 1979-ből. Akkoriban még nem voltak webalkalmazások, ezért a mai webalkalmazásokban használt MVC csupán adaptációja az eredeti ötletnek.[13]
3.3.5 Az MVC megvalósítása PHP-ban Az MVC megvalósítása PHP nyelven nagyon egyszerű. A kliens és a kiszolgáló (általában) külön gépen fut, ezért a vezérlő nem foglalkozik a nézet által kiváltott eseményekkel, ezeket a JavaScript kód kezeli. A vezérlő és a nézet közötti kommunikáció egyirányú azt az egy esetet leszámítva, amikor a nézet AJAX[14] kéréssel fordul a kiszolgálóhoz. Ez utóbbi csak akkor indokolt, ha egy művelethez a kiszolgáló oldalon tárolt adatokra van szükség vagy ha az adott
17
műveletet (vagy annak egy részét) csak a kiszolgáló tudja megvalósítani. A PHP implementációkban jellemző a nézet passzivitása a vezérlővel szemben, ezért az itt megvalósított MVC mintákat sokan MVP-nek (Model View Presenter) [15] nevezik. A MVPbeli presenter pont abban különbözik az MVC vezérlőjétől, hogy az csak egy irányban kommunikál a nézettel. Az esetleges AJAX lekérések miatt nem feltétlenül helytálló minden MVC implementációt MVP-nek nevezni.
3.3.6 Az MVC megvalósítása CodeIgniterben A CodeIgniter egy PHP keretrendszer (framework). Az ilyen keretrendszereket a CMS-ekkel (Content Management System) ellentétben nem átlagos felhasználói ismeretekkel rendelkezőknek találták ki, hanem webfejlesztőknek. A PHP keretrendszerek elsődleges célja a webfejlesztők munkájának leegyszerűsítése, így az MVC megvalósítás mellé rengeteg osztályt kapunk, amelyek a mindennapos webfejlesztési munkát segítik. A CodeIgniter-ben egy-egy mappába kerülnek a nézetek, vezérlők és modellek PHP fájlok formájában. A modellek és vezérlők származtatott PHP osztályok, a nézetek pedig .php kiterjesztésű HTML fájlok. A nézetek természetesen tartalmazhatnak PHP kódot, ennek segítségével lehet adatokat tölteni egy nézetbe. Elágazások és ciklusok is használhatóak, amennyiben erre van szükség, de érdemes a PHP kódot minimalizálni a nézetekben is úgy, ahogy a vezérlőkben. Ki kell jelölnünk egy alapértelmezett vezérlőt, amelyet akkor tölt be a keretrendszer, ha a főoldalt látogatja meg a felhasználót. Több más adat mellett ezt is a konfigurációs fájlok egyikében kell megadni.
3.4 A megvalósítás részletei Az alábbiakban leírom az implementáció részleteit. Az itt feltüntetett kódrészletek csak a megvalósítás jobb megértését szolgálják. Törekszem ezen kódrészletek mennyiségét minimalizálni a redundancia elkerülése végett.
3.4.1 A kiszolgáló oldali implementáció A szerver oldali megvalósítás fájljai az application mappában találhatóak. Ezen belül a nézetek a views, a vezérlők a controllers, a modellek pedig a models mappában vannak. A 18
kiszolgáló oldal a következő fájlokból épül fel: Vezérlők: •
page.php Ez a vezérlő arra alkalmas, hogy egyszerű HTML nézeteket jelenítsen meg. Ilyen például a Segítség oldal (/page/help).
•
szakdolgozat.php Ez a vezérlő tölti be a program felületét és ez intézi a bemeneti adatok feldolgozását. Az adatfeldolgozás része a bemenő adatok ellenőrzése és átalakítása olyan formájúvá, amelyet a JavaScript kliens képes feldolgozni. Hibás beviteli adatok esetén a megfelelő hibaüzenetet kell megjelenítenie.
Az implementált vezérlők A CodeIgniter keretrendszerben a vezérlőket a CI_Controllerből kell származtatni. A vezérlőkben kiemelt szerepet kapnak azok a metódusok, melyek feladata egy nézet betöltése (ezeket a page kulcsszóval jelöltem a fenti diagramon). Minden ilyen nézet egy külön URLen érhető el, amelynek szerkezete a következő: /vezérlő_neve/metódus_neve/arg_1/arg_2... Látható, hogy az URL tartalmazza a keretrendszer által meghívandó vezérlő nevét, azon
19
belül a metódus nevét és az annak átadandó argumentumokat. Ez alól az egyetlen kivétel a Szakdolgozat osztályban lévő save() metódus, melynek feladata a kliens oldalról érkező AJAX hívások feldolgozása. A View_Loader osztályt a nézetek betöltésének általános kezelésére hoztam létre, így ez csak egyetlen statikus függvényt tartalmaz, amelyet mindkét vezérlő használ. •
View_Loader osztály: •
load_page(Object, String, Array) A load_page függvény feladata, hogy betöltsön egy nézetet a megfelelő adatokkal feltöltve. Az első argumentumként a függvény a hívó fél (ami egy vezérlő) this paraméterét várja (ezen keresztül tud nézetet betölteni). A második argumentum egy string, amelyben megnevezhető a betölteni kívánt nézet. A harmadik pedig egy tömb, amellyel a nézetet fel tudjuk tölteni adatokkal.
•
Szakdolgozat osztály: •
__construct() Ez a szakdolgozat vezérlő konstruktora. A feladata csupán annyi, hogy betöltse a vezérlőhöz tartozó modellt.
•
check_post() Ez a függvény végzi az űrlapon átadott adatok helyességének ellenőrzését.
•
check_post_var(String, String, Boolean) Ez a függvény egy megadott nevű POST változó helyességét ellenőrzi. Az első argumentum a POST változó azonosítója, a második a hibaüzenetben megjelenő neve. A harmadik pedig egy logikai változó, amely azt jelöli, hogy a függvény ellenőrizze-e, hogy a bementi érték szám és 0-nál nagyobb. A függvény igazzal tér vissza, ha az adott POST változó ki van töltve és a tartalma megfelelő, ellenkező esetben kivételt dob egy hibaüzenettel.
•
process_post()
20
Ez a metódus a POST változók feldolgozását végzi. Tömbökbe rendezi a felhasználó által az űrlapon kitöltött adatokat. A Page osztály csak triviális metódusokat tartalmaz, így azokat nem kommentálom külön. Modellek: •
xml_model.php Az egyetlen modell fájl az alkalmazásban. Az XML fájlok feldolgozását és készítését végzi.
Az XML feldolgozó modell Látható, hogy a vezérlőkhöz hasonlóan a modelleket is származtatni kell egy beépített osztályból, ez a CI_Model. Itt néhány olyan metódus lett protected elérésű, amelyek egyébként privátak lettek volna. Erre azért van szükség, hogy elősegítse az egységtesztelést, amelyet a későbbiekben tárgyalunk. •
XML_Model osztály: •
String assoc_to_xml(Array, Integer, String) Ennek a függvénynek a segítségével lehet tömbből XML szöveget előállítani. Az első argumentum tartalmazza az XML-ben tárolandó adatokat. Ez egy többdimenziós asszociatív tömb, amely természetes számokkal indexelt tömböket is tartalmazhat. Speciális szerepe van azoknak az asszociatív tömböknek, amelyek tartalmazzák az xml_name és xml_array kulcsokat. Ez esetben az xml_array kulcshoz rendelt érték egy közönséges, természetes számokkal indexelt tömb. A függvény feladata ekkor
21
az, hogy a közönséges tömb kulcsait az xml_name szöveggel helyettesítse. Az xml_name és xml_array csupán meta adatként szolgálnak a függvény számára, azokat a végső XML-ben nem fogjuk viszontlátni. Ez azért szükséges, mert az XML szabvány szerint a numerikus karakterrel kezdődő mezőnevek nem elfogadottak. Minden közönséges tömböt így kell megadni, különben a függvény nem szabványos XML-t generál. A második argumentumként egy egész számot vár a függvény, amely azt jelöli, hogy milyen mélyen járunk a rekurzióban. Erre azért van szükség, mert tagolásképp a függvény tabulátorokat szúr az XML szövegbe, annyit amennyi a mélység, vagyis ez az argumentum. Alapértelmezett értéke: 1. A harmadik argumentum egy string, amely akkor lehet hasznos, ha kívülről szeretnénk befolyásolni a numerikus indexű tömbök kulcsait. A függvény akkor veszi figyelembe ezt az argumentumot, amikor az xml_array ki van töltve, de az xml_name nem. Ekkor a függvény harmadik argumentuma kerül felhasználásra névként. Az alapértelmezett értéke: „unkown”. A függvény visszatérési értéke az összeállított XML string. •
void check_xml_file(Object) Ez a függvény azt ellenőrzi, hogy a megadott XML fájl a megfelelő mezőket tartalmazza-e. Ezenkívül azt is, hogy a mezők a megfelelő értékekkel rendelkeznek-e vagy sem. Ennek megvalósításaként többször is meghívja a check_xml_node metódust, amely egy adott mezőt vizsgál és kivételt dob, amennyiben nem a megfelelő érték van a mezőben vagy ha a mező nem is létezik. A check_xml_file nem kapja el a kivételeket, így azokat a hívó félnek kell kezelnie. Az implementációból adódóan egyszerre csak egy hibaüzenetet kap a felhasználó még akkor is, ha több hiba van.
•
Boolean check_xml_node(Object, String, Boolean) A check_xml_node az első argumentumaként megadott XML objektumot ellenőrzi, hogy az létezik-e. Amennyiben a harmadik argumentumban megkapott logikai változó igazra van állítva, a függvény azt is ellenőrzi, hogy a mezőben lévő érték nemnegatív egész szám-e.
22
A második argumentumként megadott string-et a függvény a kivételekben megadott hibaüzenetben használja fel, amiben tájékoztatja a felhasználót, hogy melyik mező akadt fenn az ellenőrzésen. Ez az argumentum csak a mező nevét tartalmazza. Ennek megfelelően ezt az argumentumot úgy kell kitölteni, hogy a felhasználó számára világos legyen, hogy milyen mezőről van szó. Ha a függvény hibát talál a bemenetben, kivételt dob, ellenkező esetben igazzal tér vissza. •
Array extract_memory_data(Object) A memóriaszintekre vonatkozó adatokat választja külön egy tömbbe ez a függvény. Egyetlen argumentumaként a teljes XML objektumot várja. Kizárólag ellenőrzött objektumot szabad átadni a függvénynek, mert ez a metódus már nem foglalkozik a bemenet ellenőrzésével. A függvény asszociatív tömbként adja vissza a memóriaszintek adatait.
•
Array extract_object_data(Object) Az előző függvényhez hasonlóan működik ez a metódus is, azzal a különbséggel, hogy ez az objektumokra vonatkozó adatokat adja vissza asszociatív tömb formájában.
•
Array extract_optimal_solution(Object) Az előzőekhez hasonlóan működik ez a függvény is, az optimális megoldás részt adja vissza. Ez a metódus viszont végez ellenőrzést, ugyanis az XML fájl ezen része opcionális (csak abban az esetben van kitöltve, ha a felhasználó egy elmentett konfigurációt akar betölteni, amelyhez az optimális megoldást is elmentette).
•
Boolean is_xml(String) Ez a metódus ellenőrzi, hogy a bemeneti string érvényes-e az XML szabványnak megfelelően.
•
Array process_xml(String) A process_xml függvény végzi a bemeneti XML fájlok feldolgozását. A függvény XML string-et kap bemenetként, objektummá alakítja, majd a megfelelő 23
metódusok meghívásával ellenőrzi, hogy a szöveg megfelel-e az XML szabványnak és hogy a benne lévő adatokat elfogadja-e a program. Amennyiben mindent rendben talál a függvény, az XML-t asszociatív tömbbé alakítja, szintén segédfüggvények alkalmazásával, majd ezzel tér vissza. Az első argumentumban kapja meg a függvény az XML fájl tartalmát. A hibás bemenetek miatt felmerülő kivételekkel a függvény nem foglalkozik. Sőt, amennyiben a fájl nem szabványos XML-t tartalmaz, ez a függvény is dobhat kivételt. •
String save_as_xml(String, Array) Ez a függvény egy asszociatív tömböt alakít egy XML fájllá az assoc_to_xml függvény segítségével. Az első argumentum az elmentendő fájl nevét jelöli, kiterjesztés és elérési út nélkül. A második argumentum az XML fájl tartalma asszociatív tömbként. A visszatérési érték az újonnan létrehozott fájl nevét adja vissza, kiterjesztéssel és elérési úttal együtt.
A fenti dokumentációban jelöltem a metódusok bemeneti argumentumainak és visszatérési értékeinek típusát, de a PHP egy gyengén típusos nyelv, így az ezektől eltérő típusú argumentumokat is elfogadja. A függvényeknek csak akkor kell rendeltetésszerűen működniük, ha a megfelelő típusú argumentumot kapják bemenetként, ellenkező esetben PHP hibaüzeneteket kaphatunk eredményként. Nézetek: •
page.php Ez a nézetfájl egy HTML oldalt tartalmaz, amely nincs feltöltve tartalommal. A page vezérlő feladata, hogy tartalmat helyezzen bele.
•
szakdolgozat.php Ez a fájl tartalmazza a program felületét a megfelelő PHP kóddal, amely feltölti a HTML-t adatokkal.
•
error_message.php Ennek az egyszerű nézetfájlnak csupán annyi a feladata, hogy megjelenítsen egy 24
hibaüzenetet. •
form.php Ez a nézet írja le azt az űrlapot, amelyen minden beviteli adatot megadhatunk.
•
help.php Egy egyszerű HTML fájl, amely segítséget nyújt a felhasználóknak. Akkor jelenik meg, ha a felhasználó a menüben a Segítség gombra kattint, vagy meglátogatja a /page/help oldalt.
•
szakdolgzat_tema.php Szintén egy PHP kódot nem tartalmazó, egyszerű nézetfájl. A szakdolgozat címét és teljes leírását tartalmazza.
•
unit_tests.php Ez a nézet fogja megjeleníteni az egységtesztelés eredményét.
•
xml_file.php Ez az űrlap az XML fájlok feltöltésére alkalmas.
•
xml_structure.php Ebben a HTML fájlban van leírva a felhasználók számára, hogy hogyan épül fel egy XML fájl, amelyet a program elfogad.
3.4.2 A kliens oldali implementáció A JavaScript implementáció az assets/js mappában található. A következő fájlokból áll: •
form.js Ez a JavaScript fájl kezeli azt az űrlapot, amelyen minden adatot megadhatunk. A fájl fontos szerepet tölt be, mert enélkül nem lehet új memóriaszintet vagy objektumot hozzáadni a bevitelhez.
•
form_validator.js Ez a fájl tartalmazza azon eseményeket és függvényeket, amelyek az űrlap ellenőrzéséhez szükségesek.
•
mem_level.js 25
Ez az osztály a memóriaszintek megvalósítása. Tartalmazza a memóriaszintek összes tulajdonságát és azt, hogy az adott memóriaszint mely objektumokat tárolja. •
object.js Ez az osztály egy objektumot ír le.
•
optimization.js Az optimization.js tartalmazza az optimalizáló eljárást és az ahhoz tartozó segédfüggvényeket.
•
project.js Egy project objektum egy megkezdett munkát reprezentál. Tartalmazza az összes memóriaszintet és objektumot. Ebben a fájlban főként a program logikája van implementálva. A megjelenítéssel a szkript nem foglalkozik.
•
script.js Ennek a fájlnak a feladata a projekt elindítása. Példányosítja a projekt objektumot és implementálja a megjelenítést.
Fontos figyelembe venni, hogy a kliens oldali implementációnál felhasználtam a jQuery library-t. Ezt azért láttam szükségesnek, mert ezzel leegyszerűsíthettem a kliens oldali kódot, ezáltal a kód rövidebb, könnyebben áttekinthető és érthetőbb lett. A jQuery a jquery.js fájlban található.
3.4.3 Az optimalizáló eljárás implementációja Az optimalizáló eljárás egy rekurzív függvény, amely egy NP-teljes feladatot old meg. A függvényt kliens oldalon, JavaScript nyelven valósítottam meg. A hatékonyság szempontjából ez nem előnyös, de a szakdolgozatom csupán demonstrációs célokat szolgál. Amennyiben a program kereskedelmi felhasználásra került volna, az optimalizáló eljárást egy gépi kódra fordított nyelven valósítottam volna meg, amely a kiszolgáló oldalon fut. Utóbbi megoldás kényelmetlen lett volna, mert a tárhely szolgáltatók általában nem engedélyezik, hogy programot futtassunk a kiszolgálójukon. Emiatt elveszett volna az az előny, amiért a PHP és JavaScript kombinációt választottam a szakdolgozatomhoz: a programot nem kell telepíteni, elég hozzá egy számítógép, egy internetkapcsolat és egy böngésző.
26
Az eljárás megvalósításához két „trükköt” is alkalmaznom kellett. Az elsőt a rekurziós hívásnál alkalmaztam. Itt nem közvetlenül hívtam meg az eljárást, hanem a setTimeout függvénynek adtam át egy olyan anonim függvényt, amely a rekurziós hívást valósítja meg. A setTimeout egy ezredmásodpercben meghatározott időn belül hívja meg azt a függvényt, amelyet átadunk neki, ezt az én esetemben nullára állítottam. Erre – az egyébként feleslegesnek tűnő – műveletre azért van szükség, mert ha közvetlenül hívnám meg a függvényt, akkor az túl sok erőforrást fordítana a böngésző az optimalizáló eljárás futtatására, emiatt adott esetben késve reagálna a felhasználó által kiváltott eseményekre. Másik ok, hogy a beépített JavaScript stack nagyon hamar betelik, így a nagyobb bemeneteknél szinte garantált, hogy az eljárást JavaScript hibával terminálna. Egy manuális stack-et használtam egy tömb formájában, melynek kezeléséhez a beépített push és pop műveleteket hívtam meg. A másik „trükk”, amelyet az egész optimalizáló osztályban használtam, hogy a this kulcsszót helyettesítettem egy self nevű változóval, amelyet a this értékére inicializáltam. Ez azért elkerülhetetlen, mert ha argumentumként adok át egy függvényt egy másiknak (például a setTimeout függvény esetében), akkor a kontextusán kívülre került this kulcsszó már nem az optimalizáló osztályra hivatkozik, ez pedig hibát okoz. A stack három változóból áll: a contained_by, a state és a used_space. A state egy egész számot tartalmazó változó, a másik kettő egy-egy tömb. A tömbökben a megoldás egy részletét tárolom, ahol a contained_by azt jelöli, hogy egy adott indexű objektumot melyik memóriaszint tartalmazza, a used_space pedig azt, hogy a memóriaszintek hány bájt adatot tárolnak éppen. Ezek a tömbök az esetek többségében nincsenek teljesen kitöltve, a state jelzi, hogy hány objektumhoz találtunk eddig memóriaszintet. A used_space tömbre nem lenne feltétlenül szükség, hisz az általa tárolt adatokat ki lehet számolni bármikor, de ez egy további lineáris művelet végrehajtását jelentené minden függvényhíváskor, ezzel tovább lassítva az amúgy is nagyon lassú eljárást. A függvény feladata alapvetően az, hogy a soron következő objektumhoz találjon egy memóriaszintet, amelyben elhelyezheti azt. Ha talált egy olyan szintet, amelyben van elég hely az objektum eltárolásához, akkor azt rögzíti a contained_by tömbben (a used_space tömbben pedig az objektum méretével növeli a memóriaszintben foglalt területet) és meghívja önmagát az így összeállított új stack-el. Ha pedig nem talált olyan memóriaszintet, amelyben 27
az objektum elhelyezhető, akkor egy szinttel visszalép és az előző objektumot megpróbálja egy másik szinten elhelyezni. Amennyiben a tömbök megteltek, kaptunk egy új megoldást, amelyről el kell döntenünk, hogy mennyire hatékony, ezt az evaluate_solution függvény végzi. Az osztály megjegyzi az eddigi legjobb és legrosszabb megoldásokat. Utóbbit azért, mert ez alapján számíthatjuk ki a felhasználó által megadott konfiguráció hatékonyságát (a legrosszabb lehetséges megoldás a 0%-os hatékonyságú konfiguráció lesz). Az eljárás ebből adódóan az összes lehetőséget végignézi, más megoldás a feladatra nincs.
Az optimalizáló eljárás Másik megjegyzendő részlet, hogy a steps változóban követem a megtett és kihagyott 28
lépések számát, ahol egy lépés egy konfigurációnak felel meg, még akkor is, ha az adott konfiguráció nem érvényes (az objektumok nem férnek el egy memóriaszinten – ezek a kihagyott lépések). Ha egy új megoldást találunk, akkor eggyel növeljük a számlálót, ha pedig egy objektum nem fér bele egy memóriaszintbe (az egyik ágon nem tudjuk folytatni a rekurziót), akkor pedig annyival kell növelnünk a számlálót, ahány konfigurációt hagytunk ki. Ez ahhoz vezet, hogy a felhasználói felületen megjelenő progress bar nem egyenletesen növekszik: „ugrálhat” vagy huzamosabb időre megállhat egy adott ponton, de az implementációból adódóan ezen nem lehet javítani.
3.4.4 A felhasználói felület A felhasználói felület összeállításánál arra törekedtem, hogy a lehető legtöbb eszközön megfelelően jelenlen meg a weboldal. Egy úgynevezett fluid felhasználói felületet hoztam létre, amely minden eszköz képernyőjéhez alkalmazkodik, legyen az egy kis felbontású képernyővel rendelkező okostelefon vagy egy Full HD-s monitor.
A felület kisebb szélességű böngészőablakban A felület teszteléséhez nem szükséges több eszközt igénybe vennünk. Elég, ha a böngészőnk ablakát átméretezzük. A weboldal szerkezete a böngészőablak szélességének függvényében 29
fog változni. Ehhez elég csupán CSS-t alkalmazni. Az úgynevezett média lekérdezések (media query) használatával felülírhatjuk az alapértelmezett stíluslap beállításokat, ha a böngésző ablakának szélessége egy bizonyos tartományba esik.
Media query példa A képen látható példában a main azonosítójú div szélessége 300 pixel. Ezt írjuk felül 150 pixelre, ha a böngészőablak szélessége kisebb vagy egyenlő, mint 600 pixel. Ez még önmagában nem elég, hiszen azt ezzel még nem tudjuk szabályozni, hogy a div-ek hova kerülnek. Egy bizonyos pont után érdemes egymás alá tenni az egyébként egymás mellett elhelyezkedő div-eket. A következő példa ennek megoldását mutatja be:
30
Példa a div-ek helyzetének változtatására A fenti példában a left és right azonosítójú div-ek egymás mellé kerülnek amiről a float tulajdonság gondoskodik. Látható, hogy 450 és 600 pixel közötti szélességű böngészőablak esetében viszont a float tulajdonság fülöl van írva. Így a két div már nem egymás mellé, hanem egymás alá fog kerülni, attól függően, hogy a HTML dokumentumban melyik szerepel előbb.
3.4.5 1140.css Egyedi média lekérdezések megírása helyett úgy döntöttem, hogy az 1140.css grid rendszert fogom használni. Az 1140.css a fent bemutatott módon működik, egy előre megírt CSS fájl amellyel a webfejlesztők egyszerűen szerkeszthetnek fluid webes felületeket. Azért nevezik ezt grid-nek (rács), mert az elgondolás alapja az, hogy a weboldalt oszlopokra és sorokra osztja. Tetszőleges mennyiségű sort hozhatunk létre, de az oszlopok száma legfeljebb 12 lehet. A weboldal szélessége pedig legfeljebb 1140 pixel (innen adódik a név).
12 oszlopos felosztások (forrás: http://www.1140px.com/) A fenti példában 6 sort láthatunk, egy-egy felosztás szemléltetésével. Az elevezéseknek megfelelően a col1 (ami egy CSS osztály) pontosan egy oszlop szélességét veszi fel. Értelemszerűen a col2 két oszlopot jelent, a col3 hármat stb. A szabály az, hogy minden sorban ki kell tölteni a 12 oszlopos szélességet, függetlenül attól, hogy ezt két 6-os oszloppal vagy három 4-essel oldjuk meg. Az én esetemben 3 oszlopnyi szélességet foglal el a bal oldali menü és a felette lévő felirat,
31
valamint 9 oszlop szélességű a fenti menü és a tartalom rész.
A 3 és 9 oszlop szélességnyi felosztás
3.4.6 Kommunikáció a kiszolgáló és kliens között A szerver és a kliens közötti kommunikáció túlnyomó része egyszerű HTTP kérésekkel valósul meg. Az egyetlen kivétel ez alól a mentés lehetősége, ahol a kliens egy AJAX (Asynchronous JavaScript and XML) kéréssel fordul a kiszolgálóhoz. Erre a megoldásra azért van szükség, mert különben az oldal újratöltődne, ami nem szerencsés munka közben.
3.5 Tesztelés A tesztelést két részben végzem el, az egyik a felhasználói esetek tesztje, a másik az egységtesztelés. Az előbbi esetben azt ellenőrzöm, hogy a felhasználó által kiváltott eseményekre a megfelelő választ adja-e a program. Az utóbbi esetben pedig az implementáció metódusait vizsgálom. Ezekből is csak azokat, melyeknek nem egyértelmű a megvalósításuk (például nem getter vagy setter függvények).
3.5.1 Felhasználói esetek tesztelése 3.5.2 A főoldal eseményei Teszt Eset# Kiváltott esemény
Várható eredmény
Sikeresség
1
A felhasználó az XML fájl Megjelenik az XML fájlok Sikeres feltöltése gombra kattint. feltöltésére szolgáló űrlap.
2
A felhasználó az Űrlap kitöltése Megjelenik az az űrlap, amelyen Sikeres gombra kattint. minden adat bevihető.
32
3.5.3 XML fájl feltöltés események Teszt Eset# Kiváltott esemény
Várható eredmény
Sikeresség
1
A felhasználó a Fájl Megjelenik a fájlböngésző, ahol Sikeres kiválasztása gombra kattint. a felhasználó kiválaszthatja a feltölteni kívánt fájlt.
2
A felhasználó az űrlapon Az oldal újratöltődik megpróbál feltölteni egy nem hibaüzenettel. XML kiterjesztésű és nem XML tartalmú fájlt.
egy Sikeres
3
A felhasználó az űrlapon Az oldal újratöltődik megpróbál feltölteni egy XML hibaüzenettel. kiterjesztésű, de nem XML tartalmú fájlt.
egy Sikeres
4
A felhasználó az űrlapon megpróbál feltölteni egy XML kiterjesztésű, szabványos XML fájlt amelyből hiányzik legalább egy olyan adatmező, amelyre a programnak szüksége van.
5
A felhasználó az űrlapon A program betölti a felületet az Sikeres megpróbál feltölteni egy nem XML fájlban tárolt adatok XML kiterjesztésű, de alapján. szabványos XML fájlt amely tartalmaz minden adatot amire a programnak szüksége van.
6
A felhasználó az űrlapon A program betölti a felületet az Sikeres megpróbál feltölteni egy XML XML fájlban tárolt adatok kiterjesztésű, szabványos XML alapján. fájlt amely tartalmaz minden adatot amire a programnak szüksége van.
7
A felhasználó megpróbál A program betölti a felületet az Sikeres feltölteni egy korábban XML fájlban tárolt adatok elmentett konfigurációt. alapján és az elmentett konfigurációt is megjeleníti.
8
A felhasználó megpróbál feltölteni egy korábban elmentett konfigurációt amely az optimális megoldást is tartalmazza.
Az oldal újratöltődik egy Sikeres hibaüzenettel ahol a program jelzi, hogy mely adatmezők hiányoznak a fájlból.
A program betölti a felületet az Sikeres XML fájlban tárolt adatok alapján és az elmentett konfigurációt is megjeleníti. Ebben az esetben a program nem kezdi el az optimalizáló eljárás végrehajtását.
33
3.5.4 Űrlap események Az űrlap esetében a kötelezően kitöltendő mezőkbe mindig pozitív egész számoknak kell kerülniük. A továbbiakban a kötelezően kitöltendő mezőkként fogok hivatkozni a méret, az elérési sebesség, a példányszám és az elérések száma mezőkre. Először a kliens oldali űrlap ellenőrzést tesztelem: Teszt Eset# Kiváltott esemény
Várható eredmény
Sikeresség
1*
A felhasználó belekattint egy A program figyelmezteti a Sikeres kötelezően kitöltendő mezőbe, felhasználót, hogy a mezőt oda nem ír semmit, majd egy kötelező kitölteni. másik mezőbe kattint.
2*
A felhasználó belekattint egy A program figyelmezteti a Sikeres kötelezően kitöltendő mezőbe, felhasználót, hogy a mezőbe oda többek között betűket is ír, csak numerikus érték kerülhet. majd egy másik mezőbe kattint.
3*
A felhasználó belekattint egy A program figyelmezteti a Sikeres kötelezően kitöltendő mezőbe, felhasználót, hogy a mezőbe oda egy nem pozitív egész csak pozitív érték kerülhet. számot ír, majd egy másik mezőbe kattint.
4*
A felhasználó belekattint egy kötelezően kitöltendő mezőbe, oda egy lebegőpontos értéket ír, majd egy másik mezőbe kattint.
5
A felhasználó egy teljesen üres A program figyelmezteti a Sikeres űrlapot küld el. felhasználót, hogy nem töltötte ki az összes kötelezően kitöltendő mezőt.
6
A felhasználó gondosan kitölti A program figyelmezteti a Sikeres az űrlapot egy kötelezően felhasználót, hogy nem töltötte kitöltendő mező kivételével. ki az összes kötelezően kitöltendő mezőt.
7
A felhasználó az Új Egy új sornyi mező jelenik meg, Sikeres memóriaszint gombra kattint. ahol egy új memóriaszint értékeit adhatja meg a felhasználó.
8
A felhasználó az Új objektum Egy új sornyi mező jelenik meg, Sikeres gombra kattint. ahol egy új objektum értékeit adhatja meg a felhasználó.
9
A felhasználó a memóriaszintek Nem történik semmi. alatt lévő Legutolsó törlése
A program figyelmezteti a Sikeres felhasználót, hogy a mezőbe csak pozitív egész érték kerülhet.
34
Sikeres
gombra kattint úgy, hogy előtte nem adott hozzá új memóriaszintet. 10
A felhasználó az objektumok Nem történik semmi. alatt lévő Legutolsó törlése gombra kattint úgy, hogy előtte nem adott hozzá új objektumot.
11
A felhasználó a memóriaszintek A legutolsó sort a program törli. Sikeres alatt lévő Legutolsó törlése gombra kattint úgy, hogy előtte hozzáadott egy új memóriaszintet.
12
A felhasználó az objektumok A legutolsó sort a program törli. Sikeres alatt lévő Legutolsó törlése gombra kattint úgy, hogy előtte hozzáadott egy új objektumot.
35
Sikeres
* A csillaggal jelölt eseteket azokra mezőkre is alkalmazni kell, amelyeket az Új memóriaszint és az Új objektum gombokkal lehet hozzáadni az űrlaphoz. A programnak akkor is megfelelő válaszokat kell adnia a felhasználónak, ha a böngészőjében ki van kapcsolva a JavaScript, a következő táblázatban ezt ellenőrzöm: Teszt Eset# Kiváltott esemény
Várható eredmény
Sikeresség
1
A felhasználó betölti az űrlap A program figyelmezteti a Sikeres oldalt úgy, hogy a felhasználót, hogy JavaScript böngészőjében ki van kapcsolva nélkül nem fog működni. a JavaScript.
2
A felhasználó elküldi az űrlapot Az oldal újratöltődik egy Sikeres úgy, hogy egy kötelezően hibaüzenettel, amely megnevezi kitöltendő mezőt kihagy. a kitöltetlen mezőt.
3
A felhasználó elküldi az űrlapot úgy, hogy egy kötelezően kitöltendő mezőbe negatív értéket ír, de a többi mezőt megfelelően kitölti.
Az oldal újratöltődik egy Sikeres hibaüzenettel, amely tájékoztatja a felhasználót a hiba okáról és a nem megfelelően kitöltött mező nevéről.
4
A felhasználó elküldi az űrlapot úgy, hogy egy kötelezően kitöltendő mezőbe lebegőpontos értéket ír, de a többi mezőt megfelelően kitölti.
Az oldal újratöltődik egy Sikeres hibaüzenettel, amely tájékoztatja a felhasználót a hiba okáról és a nem megfelelően kitöltött mező nevéről.
5
A felhasználó elküldi az űrlapot úgy, hogy egy kötelezően kitöltendő mezőbe alfanumerikus értéket ír, de a többi mezőt megfelelően kitölti.
Az oldal újratöltődik egy Sikeres hibaüzenettel, amely tájékoztatja a felhasználót a hiba okáról és a nem megfelelően kitöltött mező nevéről.
3.5.5 A program felületének eseményei A továbbiakban a program felületét tesztelem. Feltételezem, hogy az optimalizálás még nem fejeződött be, így a felhasználó számára bizonyos funkciók nem elérhetőek. Teszt Eset# Kiváltott esemény
Várható eredmény
Sikeresség
1
A felhasználó megfog egy Az objektum (animálva) Sikeres objektumot és drag-n-drop visszamegy az eredeti helyére. módszerrel egy üres területen (a világoskék területen kívül) engedi el.
2
A felhasználó megfog egy Az objektum (animálva) Sikeres objektumot és drag-n-drop visszamegy az eredeti helyére. 36
módszerrel egy olyan memóriaszinten próbálja meg elhelyezni, amelyen nincs elég szabad hely hozzá. 3
A felhasználó megfog egy (memóriaszinten még nem elhelyezett) objektumot és dragn-drop módszerrel és egy olyan memóriaszint fölött engedi azt el, ahol van elég hely az objektum eltárolásához.
• Az objektumot a program Sikeres animálva elhelyezi a memóriaszinten a többi objektum mellett úgy, hogy az összes objektum adatai láthatóak legyenek (ne fedjék egymást). • A memóriaszinten felhasznált terület az objektum méretével nő. • A program megadja (százalékban, 0 tizedesjegyre kerekítve) a szabad és elfoglalt terület arányát a memóriaszinten.
4
A felhasználó megfog egy • Az objektumot a program Sikeres (memóriaszinten már animálva elhelyezi az új elhelyezett) objektumot és drag- memóriaszinten a többi n-drop módszerrel egy másik, objektum mellett úgy, hogy az olyan memóriaszint felett engedi összes objektum adatai el, amelyben van elég hely az láthatóak legyenek (ne fedjék objektum tárolásához. egymást). • Az új memóriaszinten felhasznált terület az objektum méretével növekszik, a régi memóriaszinten pedig ugyanilyen mértékben csökken. • A program megadja (százalékban, 0 tizedesjegyre kerekítve) a szabad és elfoglalt terület arányát az új és a régi memóriaszinten is.
5
A felhasználó megfog egy • Az objektumot a program Sikeres (memóriaszinten már animálva elhelyezi az területen elhelyezett) objektumot és drag- úgy, hogy az összes objektum n-drop módszerrel a nem adatai láthatóak legyenek (ne elhelyezett objektumok számára fedjék egymást). fenntartott területen engedi el. • A memóriaszinten (melyen az objektum elhelyezkedett) a felhasznált terület az objektum méretével csökken. 37
• A memóriaszinten a szabad és felhasznált terület aránya újraszámolódik (százalékban, 0 tizedesjegyre kerekítve). 6
A felhasználó a Mentés gombra A böngésző elkezd letölteni egy Sikeres kattint. .xml kiterjesztésű fájlt, amely tartalmazza a projektben felhasznált objektumokat és memóriaszinteket, valamint a felhasználó által megadott konfigurációt. Mindezt úgy, hogy az megfelel az XML szabványnak és ahogy azt a korábbiakban a felhasználói- és fejlesztői dokumentáció definiálta.
Az optimalizációs eljárás befejeztével több opció áll a felhasználó rendelkezésére. Most ezeket tesztelem: Teszt Eset# Kiváltott esemény
Várható eredmény
1
Egy kivételével minden objektum a memóriaszintek valamelyikén helyezkedik el. A felhasználó az utolsó objektumot is elhelyezi egy memóriaszinten.
A program kiszámítja a Sikeres felhasználó által megadott konfiguráció hatékonyságát, egész számra kerekítve és százalékban megadva. A számítás során figyelembe kell venni a legkevésbé hatékony megoldást (ez számít a 0%-os megoldásnak) és a legoptimálisabb megoldást (ez pedig a 100%-os megoldás).
2
Az összes objektum el van A program a hatékonyságot nem Sikeres helyezve valamelyik írja ki. memóriaszinten. A felhasználó valamelyik objektumot a nem elhelyezett objektumok számára fenntartott területre helyezi át.
3
A felhasználó a Mentés A böngésző elkezd letölteni egy Sikeres optimális megoldással gombra .xml kiterjesztésű fájlt, amely kattint. tartalmazza a projektben felhasznált objektumokat és memóriaszinteket, valamint a felhasználó által megadott konfigurációt és az optimális megoldást. Mindezt úgy, hogy 38
Sikeresség
az megfelel az XML szabványnak és ahogy azt a korábbiakban a felhasználói- és fejlesztői dokumentáció definiálta.
3.5.6 A tesztelés tanulságai A tesztelés során nagyon sok hibára bukkantam, ezekből csak néhányat említenék itt meg. •
A XML fájl feltöltése eseményei #7 Ez a teszteset vizsgálja a korábban elmentett konfigurációk betöltését. Elmentettem egy konfigurációt, majd megpróbáltam azt újra feltölteni, amikor egy hibába ütköztem: a program nem úgy érzékelte az egyik memóriaszint kapacitásának értékét, mintha az egy nemnegatív egész szám lenne. A hiba abból adódott, hogy a program más tagolással menti az XML fájlokat, mint ahogy én azokat manuálisan összeállítottam, ezért a program szövegként két szóközzel az elején és a végén olvasta ki az értékeket. Ekkor következett volna az a fázis, amikor ellenőrzöm, hogy az adott string numerikus értéket tartalmaz-e a beépített is_numeric függvénnyel, amely természetesen hamissal tért vissza minden ilyen string-re. A megoldás a beépített trim függvény használata az is_numeric függvény meghívása előtt.
•
Űrlap események #11 Ezen teszteset vizsgálata során jöttem rá arra, hogy az utolsó oszlopból, a memóriaszintek elérési sebességének mezőit a program nem törli. Ennek oka az volt, hogy korábban az űrlapot ellenőrző függvények megírásakor magát az űrlapot is módosítottam, de az utolsó sort törlő függvény erre nem volt felkészítve.
3.5.7 Egységtesztelés Látható, hogy a fenti esetekkel a program működése nagyrészt tesztelhető. De bizonyos hibák még a fenti, sok mindenre kiterjedő teszteléssel sem szűrhetőek ki. Másrészt elvárás, hogy a program továbbfejlesztése esetén az újonnan keletkezett hibák könnyen kiszűrhetőek legyenek. Erre a célra kitűnő eszköz az egységtesztelés, melynek feladata, hogy az osztályok metódusainak viselkedését tesztelje automatikusan. Ez azt jelenti, hogy vannak külön erre a
39
célra megírt egységtesztelő osztályok (minden tesztelni kívánt osztályra jut egy egységtesztelő osztály), melyek az erre a célra fenntartott oldalon futnak le. Ez az oldal a /page/unit_tests címen érhető el, de csak akkor, ha a CodeIgniter development módra van állítva. Ha development mód helyett production a beállítás, akkor az elvárható viselkedés az, hogy a főoldalra irányítsa át a program a felhasználót. A kiszolgáló oldali kódból csak az XML_Model osztályt teszteltem egységtesztekkel, de lehetőséget biztosítottam arra, hogy ez könnyen kibővíthető legyen más osztályok egységtesztjeivel is. Az egység tesztelés során fontos szempont a teszt elvégzésének sebessége, így a külső adatforrások (fájlok vagy adatbázisok) használata kerülendő. Mivel egy XML fájlokat feldolgozó osztályt tesztelek, ezért a tesztelő osztályban definiált konstans string-eket adom át a metódusoknak ahelyett, hogy azokat fájlból olvasnám. Mivel a legtöbb egység tesztelt metódus eredetileg private volt, így azt a megoldást választottam, hogy az egységtesztelő osztályt a tesztelni kívánt osztályból származtattam le, a tesztelt metódusokat pedig private-ről protected-re változtattam.
40
Az egység tesztelt és egységtesztelő osztályok diagramja Látható, hogy sok egységtesztet végez ez az osztály. Nagyobb projektek esetében ilyenkor érdemes külső könyvtárat használni az egységteszteléshez vagy egy általánosított megoldást implementálni az egységtesztelő osztályok include-jához és az azokban lévő metódusok automatikus futtatásához. PHP nyelven az utóbbira lenne is lehetőség. Ebben az
esetben
viszont nem láttam indokoltnak,
hogy túlkomplikáljam az
egységtesztelést, ezért a lehető legegyszerűbb megoldást választottam: létrehoztam egy osztályt, amely include-olja az egységtesztelő osztályt és egyenként meghívja a metódusait, majd azok eredményeit egy asszociatív tömbben tárolja el. Ennek az az előnye, hogy az asszociatív tömb kulcsaiként megnevezhetőek a tesztek.
41
Az egységtesztelést végző osztály. A Unit_Tests osztály execute_xml_model_unit_tests metódusa hívja meg az összes, XML_Model osztályhoz tartozó egységtesztet, majd eltárolja az eredményeket egy asszociatív tömbben. Az execute_unit_tests metódus pedig az egyes osztályok egységtesztjeit végző metódusokat hívja meg (jelen esetben ez csak az execute_xml_model_unit_tests), majd az azok által visszaadott asszociatív tömböket egy újabb asszociatív tömbben tárolja el, ezáltal lehetőség adódik arra is, hogy az osztályok tesztjeinek nevet adjunk.
3.5.8 Az XML_Model osztály egységtesztelése A következő táblázatban leírom, hogy miért láttam szükségesnek az egyes egységteszteket: Egységteszt neve
Tesztelés oka
process_xml Teszt #1
Ez a teszt egy olyan esetet vizsgál, amelyben egy minimális, tulajdonképpen semmilyen információt nem tartalmazó XML string-et adunk át a process_xml függvénynek. Az elvárható eredmény, hogy a függvény kivételt dob, mivel elvárja, hogy az XML a korábbiakban definiált adatokat tartalmazza.
process_xml Teszt #2
Ez a teszteset egy szabványos, minden elvárt adatot tartalmazó XML string-et ad át. Az elvárható működés az, hogy a függvény nem dob kivételt.
process_xml Teszt #3
Ez a teszt egy majdnem minden szükséges adatot tartalmazó XML string-et ad át a metódusnak, ebben az esetben is kivételt kell kapnunk.
process_xml Teszt #4
Ebben az esetben egy nem szabványos XML string kerül átadásra, szintén kivételt kell kapnunk.
is_xml Teszt #1
Ez a teszteset egy szabványos XML string-et
42
ad át az is_xml függvénynek, az elvárható visszatérési érték: igaz.
is_xml Teszt #2
Egy hibás XML string-et adunk át a függvénynek ebben az esetben, ahol egy nyitó tag-nek nincs záró párja. Hamis az elvárható visszatérési érték.
is_xml Teszt #3
Ebben az esetben egy szintaktikai hibával rendelkező XML string-et kap a függvény (hiányzik egy relációs jel), hamissal kell visszatérnie a függvénynek.
is_xml Teszt #4
A metódushoz tartozó utolsó tesztesetben egy olyan XML string-et kap, amelyben egy záró tag fel van cserélve a neki megfelelő nyitó tag-gel. A korábbiakhoz hasonlóan itt is a hamis visszatérési érték az elfogadott.
extract_optimal_solution Teszt #1
Ez a függvény egy szabványos, helyesen kitöltött XML string-et kap. A tesztesetnek azt kell vizsgálnia, hogy az XML-ben lévő adatok tömbjével tér-e vissza a függvény.
extract_optimal_solution Teszt #2
Ez a teszteset ugyanazt vizsgálja, mint az előző, de más értékekkel.
extract_memory_data Teszt #1
Az XML string ebben az esetben egy memóriaszint értékeit tartalmazza, szabványosan és megfelelően kitöltve. A függvénynek egy tömbbel kell visszatérnie az XML-ben tárolt értékekkel.
extract_object_data Teszt #1
Itt egy objektumot tartalmaz az XML. A függvény működése hasonló az előzőekhez, így itt is csak egy tömbbel kell összehasonlítani a visszatérési értéket.
43
3.5.9 A tesztesetek felülete
A tesztesetek eredményét megjelenítő felület A fenti képen látható, hogy mi jelenik meg /page/unit_tests oldalon. Ha a jövőben módosításra kerül az XML_Model osztály, illetve azon belül a tesztelt függvények, akkor csak annyi a dolgunk, hogy meglátogassuk ezt az oldalt és a hibák többsége rögtön kiderül. Ez az egyik leghatékonyabb módja a hibakeresésnek.
3.5.10 A tesztelésből levont tanulság Az egységtesztelés során ráleltem egy olyan hibára, amelyre egyébként valószínűleg nem derült volna fény. Ez az is_xml függvényt érintette, ahol a libxml könyvtárat használtam annak megállapítására, hogy a bementi string szabványos XML-t tartalmaz-e. A libxml egy átmeneti tárolóban tartja a hibákat amely nem törlődik, ha lekérdezzük a tartalmát, a törléshez a libxml_clear_errors függvényt kell használni. Az error puffer törlésére alkalmas függvényt eredetileg nem használtam, így az is_xml függvény csak az első hívásra tért vissza az elvárható értékkel.
44
3.6 Hatékonyság vizsgálat Nyilvánvalóan csak az optimalizációs eljárás esetében van értelme a hatékonyság vizsgálatnak, így csak ezt vizsgálom. A hatékonyság vizsgálatot többféle bemenetre, azokon egyenként többször is el kell végezni, majd átlagolni kell az eredményeket. Ebből adódóan kiszűrhetőek az olyan esetek, ahol a futtatott folyamat az átlagosnál kevesebb erőforrást kapott a számítógéptől. A vizsgálathoz használt számítógép hardveres és szoftveres paraméterei: •
Processzor: Intel i5 650 (2 fizikai mag, 3.2 GHz)
•
RAM: 4 GB
•
Böngésző: Google Chrome 42.0
•
JavaScript verzió: 1.7
A vizsgálat elvégzéséhez az optimization.js fájlban az optimalizáció futtatása előtt rögzítem egy változóban a pontos időbélyeget, majd az eljárás végén azt kivonom a jelenlegiből. Ezt ezerrel osztva megkapom a futásidőt másodpercben. A hatékonyságvizsgálathoz felhasznált tesztesetek a test mappában találhatóak. A továbbiakban n-el jelölöm a memóriaszintek számát, m-el az objektumok számát és k-val a lehetséges konfigurációk számát. Eset #1, fájl: test1.xml (n = 4, m = 5, k = 16) Futtatás
Futásidő (másodperc)
#1
0.318
#2
0.316
#3
0.338
#4
0.295
#5
0.301
Átlag
0,3136
Eset #2, fájl: test2.xml (n = 4, m = 9, k = 3240) 45
Futtatás
Futásidő (másodperc)
#1
47.875
#2
47.875
#3
47.962
#4
47.946
#5
47.986
Átlag
47,9288
Eset #3, fájl: test3.xml (n = 4, m = 9, k = 216) Futtatás
Futásidő (másodperc)
#1
8.052
#2
8.581
#3
8.556
#4
8.153
#5
8.128
Átlag
8,294
Az utolsó két esetből világosan látszik, hogy nem lényeges a memóriaszintek és az objektumok száma. Ezek az értékek mind a két esetben azonosak (a memóriaszintek száma 4, az objektumoké pedig 9), viszont a lehetséges konfigurációk számában nagy különbség van: míg az első esetben 3240 esetet kellett átvizsgálni a programnak, a másodikban csupán 216-ot és ez meg is látszik a mért futásidőkben. Mindez a program működéséből adódik, mivel a program nem vizsgálja azokat a konfigurációkat, amelyek nem érvényesek. Vagyis az olyan konfigurációkat, ahol az objektum(ok) nem fér(nek) bele a memóriaszint(ek)be, a program kihagyja. Eset #4, fájl: test4.xml (n = 5, m = 8, k = 1616) Futtatás
Futásidő (másodperc)
#1
30.883
#2
30.859
#3
30.879
#4
30.823
#5
31.07
46
Átlag
30,9028
Eset #5, fájl: test5.xml (n = 5, m = 8, k = 92) Futtatás
Futásidő (másodperc)
#1
1.533
#2
1.548
#3
1.53
#4
1.511
#5
1.508
Átlag
1,526
A fenti adatok alapján látható, hogy nagyjából milyen hatékony a program. Egy adott tesztfájlhoz tartozó hatékonyságot megállapíthatjuk a JavaScript konzol megnyitásával. (Chrome-ban F12 billentyű) A konzolban láthatjuk az összes megtalált optimális konfigurációt és a hozzájuk tartozó heurisztika értéket, valamint a teljes optimalizáció futásidejét és a vizsgált konfigurációk számát.
A JavaScript konzol
47
4 Hivatkozások •
[1] http://en.wikipedia.org/wiki/Memory_hierarchy (2015. 05. 10.)
•
[2] http://en.wikipedia.org/wiki/Static_random-access_memory (2015. 05. 10.)
•
[3] http://en.wikipedia.org/wiki/Dynamic_random-access_memory (2015. 05. 10.)
•
[4] http://hu.wikipedia.org/wiki/Fogd_%C3%A9s_vidd (2015. 05. 10.)
•
[5] http://hu.wikipedia.org/wiki/XML (2015. 05. 10.)
•
[6] http://www.w3.org/TR/xml/ (2015. 05. 10.)
•
[7] http://www.codeigniter.com/ (2015. 05. 10.)
•
[8] http://jqueryui.com/ (2015. 05. 10.)
•
[9] http://jquery.com/ (2015. 05. 10.)
•
[10] http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller (2015. 05. 10.)
•
[11] Jeff Atwood: „Understanding Model-View-Controller” http://blog.codinghorror.com/understanding-model-view-controller/ (2015. 05. 10.)
•
[12] http://stackoverflow.com/questions/27950039/in-mvc-should-the-view-know-themodel (2015. 05. 10.)
•
[13] Pablo Pastor: „MVC for Noobs” http://code.tutsplus.com/tutorials/mvc-for-noobs--net-10488 (2015. 05. 10.)
•
[14] http://en.wikipedia.org/wiki/Ajax_%28programming%29 (2015. 05. 10.)
•
[15] Martin Fowler: „GUI Architectures” http://martinfowler.com/eaaDev/uiArchs.html#Model-view-presentermvp (2015. 05. 10.)
48
5 Irodalomjegyzék [1]
Bauer, Michael, et al. "Programming the memory hierarchy revisited: Supporting irregular
parallelism in sequoia." ACM SIGPLAN Notices. Vol. 46. No. 8. ACM, 2011. [2]
Kandemir, Mahmut, et al. "Dynamic management of scratch-pad memory space." Proceedings of
the 38th annual Design Automation Conference. ACM, 2001. [3]
Nieplocha, Jarek, Robert Harrison, and Ian Foster. "Explicit management of memory
hierarchy." Advances in High Performance Computing. Springer Netherlands, 1997. 185-199. [4]
Kandemir, Mahmut, et al. "Dynamic management of scratch-pad memory space." Proceedings of
the 38th annual Design Automation Conference. ACM, 2001.
49