József's profilePut AboutBlogListsGuestbookMore ![]() | Help |
Put About{Quot capita, tot sententiae...} ---------------------------------->?<--------------------------------- {email: putabout@upcmail.hu} |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
July 05 .NET szemétgyűjtés - 4.A múlt héten ott hagytuk abba, hogy ígértem egy mesét. Ez a mai mese a szemétgyűjtés során végrehajtott finalizer folyamat pontos menetéről, illetve az úgynevezett freachable sor vagy eredeti nevén "freachable queue" lényegéről szól.
Az előző rész végén volt egy ábra, rajta a C, E, F, I és J objektumokkal, amelyek hivatkozása szerepel az úgynevezett finalization listában. Ez azt jelenti, hogy a hivatkozott objektumoknak van finalizer metódusa, melyekkel majd a szemétgyűjtés során komolyan törődni is kell. Amikor a szemétgyűjtés elindul, akkor a B, E, G, H és I objektumok a korábban már ismertetett módon, azonnal pusztulásra vannak ítélve. Mindeközben a CLR azért végignézi a finalization listát is, hogy megkeresse mely törlésre jelölt objektumok mutatói szerepelnek rajta. Amikor talál egyet, akkor törli ebből a listából, és átrakja a másik, úgynevezett "Freachable" sorba (ejtsd: "F-reachable" sorba). Itt tehát azok az objektumok mutatói tanyáznak, melyek megszűntetésre vannak ítélve, és milyen pech, e mellett még van finalizer metódusuk is.
A fenti ábrán ez az állapot látható illetve az is, hogy a B, G és H objektumok a szemétgyűjtés következményeképp már megszűntek (tessék összevetni a korábbi bejegyzésben lévő ábrával), ezáltal a memória helyük felszabadult, valamint a felszabadított hely tömörítése is végrehajtódott (nekik nem volt finalizer metódusuk). Azonban az E, I és J objektumok bár első körben szintén pusztulásra voltak ítélve, mégis megmaradtak. Ők még nem tudják, de mi már igen, hogy a túlélés miatti örvendezést azért nem kellene annyira túlzásba vinni, mivel ezt csakis kizárólag a létező finalizer metódusaiknak köszönhetik (ráadásul nem is tart valami sokáig).
Az objektumok említett finalizer metódusainak meghívására egy speciális, külön erre a célra fenntartott, magas prioritású CLR-szál (CLR-thread) szolgál. A külön szálnak és az említett prioritásnak az a célja, hogy elkerülje a normál prioritású szálak szinkronizációjakor felmerülő versengéseket. Amikor a freachable sor üres (gyakori eset), akkor ez a szál békésen alszik, fittyet hányva a körülötte tombóló szemétgyűjtő által végrehajtott erőszaknak. Abban a pillanatban azonban, ahogy egyetlen egy objektum mutató is bekerült a freachable sorba, a nevezett szál tüstént megindul útjára, és szépen sorban meghívja minden egyes objektum finalizer metódusát. A sikeres meghívást és végrehajtást követően az adott objektum mutatója törlődik a freechable sorból.
Bár az eddigiekből kiderülhetett, de azért mégsem árt kihangsúlyozni, hogy azok az objektumok, melyek benne vannak a freachable sorban gyakorlatilag már korábban puszulásra lettek ítélve. Ezt a programozónak a finalizer metódus írásakor illő lenne tudomásul vennie, és emiatt a túl sok, de főleg összetett és időigényes műveleteket elkerülnie (a szálkezelés, kivétel előidézés, hosszú ideig tartó vagy összetett adatbázis SQL utasítások végrehajtása nem szép dolog, de természetesen a szokásos erőforrás felszabadításokra irányuló tevékenységek mehetnek).
Megjegyzés: A "Freachable" szóból az "F" a finalizer-re, a "reachable" pedig arra utal, hogy az objektum elvileg még létezik és elérhető, hiszen a memóriabeli helye ekkor még nincs eltakarítva, felszabadítva (legalábbis néhány pillanatig biztosan nem). Ugyanígy a pillanatnyi túlélők klubjába tartoznak még azok az objektumok is, amelyekre a finalizer metódust tartalmazó objektum mezői, vagy helyi változói éppen hivatkoznak.
Amikor a szemétgyűjtés legközelebb ismét elindul látja (vagy pont nem látja, de ez már filozófiai kérdés), hogy a korábban felszabadításra jelölt objektumoknak már nincs bejegyzése a finalization listában, illetve a freachable sorban sem, így jó szamurájhoz méltóan, kénye-kedve szerint lekaszabolhatja őket. Ekkor (vagyis a következő szemétgyűjtés során), és csakis ekkor történhet meg az előző szemétgyűjtéskor már törlésre jelölt objektumok helyének tényleges felszabadítása, és az újabb memória tömörítés végrehajtása.
Kiderült tehát, hogy egy finalizer metódussal rendelkező objektum megszűntetéséhez ténylegesen legalább két szemétgyűjtési fázis szükséges. Fontos megjegyezni még, hogy a finalizer metódus elkészítését az úgynevezett "Dispose pattern" támogatja, melyet rengeteg szakirodalom taglal, ezért én most nem is szeretnék kitérni rá.
Hogy teljes legyen a kép, legközelebb a halott objektumok feltámasztásáról, és végre valahára a már sokat emlegetett generációs listákról lesz szó.
Folytatjuk... July 01 "YEAH! I use Linux! F**k Microsoft!"Csak kimondta már valaki (rajtam kívül is)! Na nem, természetesen nem a "YEAH! I use Linux! F**k Microsoft!" mondatról van szó, hanem... de ne fussunk az események elébe. Örülök, hogy legalább néhányan a Linux Journal lapjain érzik a problémát, ami bizony létezik, ráadásul igaz. Nagyon igaz. Tessék elolvasni.
"The good Linux users I know don’t even talk about Microsoft. Never will you see a "windoze", "winblows", "M$" or "Micro$oft" [esetleg Magyarországon "mikrofos"] in anything they blog about. To note, those that do write those childish things are morons because as said above, nobody cares. You don’t see me calling Linux users tux turds, penguin poopers or GUI-challenged, do you?"
Részletek itt.
Sokszor, sokat írtam már e helyütt, hogy a hithű, Linux-os rajongóknak gyerekes módon nem másokkal, nem a Microsoft-tal, hanem saját magukkal kellene foglalkozniuk. A pingvines közösség túlnyomó többsége vagánynak tartja minősíthetetlen stílusban jellemezni az ellenfeleket (aki nem velünk az ellenünk, ugye), ami persze jottányit se emeli az emberek szemében sem az ő, sem pedig a Linux ázsióját. Aki nem hiszi, olvasgasson pár napig ezen a helyen, és majd meglátja, hogy a fenti cikk írójának igaza volt. Pont ez az egyik ok, ami miatt én is irtózom tőlük, és amiért néha kissé... hmm... mondjuk úgy - erősebben nyomom a billentyűket. June 29 Statisztikák, hozzászólások és vendégkönyvNéhány évente, az új olvasók kedvéért előjövök ezzel a témával.
2005. január óta összesen 93948 látogatóm volt. Fogalmam sincs, hogy ez a mennyiség sok vagy kevés, de igazából nem is érdekes. Ha valaki olvassa jó, ha nem, akkor azt meg sose fogom megtudni. A hozzászólási lehetőségeket kezdettől fogva kikapcsoltam, a vendégkönyv bejegyzést pedig mától. Általában a webnapló írók (blogger-ek vagy kik) szeretik, sőt alig várják a hozzászólásokat, véleményeket. A magam részéről ezt a késztetést soha se érztem, mivel az írásaim célja csak annyi, hogy a saját stílusomban elmondjam, amit szeretnék. Hogy ki és mit gondol erről, az már mindenkinek a magánügye, amit bátran írjon meg a saját webnaplójában.
A vélemények mindig erősen szubjektívek, hiszen ugyanarról a dologról mindenki gondolhatja akár az ellenkezőjét is annak, mint amit én. Ha pl. azt mondanám, hogy legalább 10 ténnyel tudom igazolni, hogy a .NET platform jobb, mint a Java platform (és ezeket le is írnám), akkor nyilván sok-sok Java-rajongó esne nekem, és legalább 10 másik tényt rángatna elő a tarsolyából, amivel bizonyítaná, hogy nekem miért nincs igazam, és miért neki van igaza (legalábbis az ő szemszögéből vizsgálva). Olyan kevés dolog mérhető jól, ezért sokszor még amire rámondhatnánk, hogy objektív, bizonyos szempontból az is szubjektív attól függően ki honnan, milyen szemszögből vagy érdekből, esetleg érzelemből vizsgálja (gondoljunk csak a holokauszttagadókra és a holokauszttagadást tiltókra).
Egy biztos, az élet soha se fehér vagy fekete, és az igazság soha sem az egyik, vagy másik oldalon van. Na mindegy, hagyjuk.
Aki olvassa az írásaimat, az ezúton fogadja hálás köszönetemet a figyelemért, türelemert és megértésért, aki meg nem, annak sok sikert és jó egészséget kívánok.
Köszönettel,
PutAbout
Ui.: Aki mégiscsak írni szeretne, az a putabout@upcmail.hu címre küldheti el. Ha közédekű a tartalom és vállalható a stílus úgyis közzé teszem. Windows kernel újdonságokAz egyik kedvenc témám az operáció rendszerek - úgy általában. Soci írt egy érdekes cikket, ami remélem még csak a kezdet. Tessék elolvasni, és figyelemmel kísérni, mert érdemes.
"...Ez nagyon szép kódot eredményezett, azonban teljesítményszempontból nem túl jó a sok DLL, ezért bár belül külön kezelik a funkciókat, a végén mégis csak egybecsomagolják, ez a kernelbase.dll. Így a kód fejlesztés során jól kezelhető, a gyakorlatban mégis gyors. API set-ekként, API halmazokként hivatkoznak ezekre a szeparált funkciókra, amelyeket ún. virtuális DLL-ként valósítottak meg az előbb leírt módon. A virtuális DLL-ek fizikaira leképezését az apisetschema.dll tartalmazza. Az OS Loader, ami a futtatható állományok betöltését végzi, ezen DLL segítségével tudja, hogy egy virtuális DLL valójában mely fizikai DLL-ben lakik, így oda oldja fel a függvényhívásokat. Azaz kidolgoztak egy ügyes módszert, amely megteremti az alapját egy rendkívül moduláris Windows felépítésének, anélkül, hogy a kompatibilitást megtörnék."
Részletek itt. June 28 .NET szemétgyűjtés - 3.A múltkor bemerészkedtünk a sűrűbe egy picit tudván, hogy ott bizony ordas vadállatok leselkednek ránk. Minket azonban a bátrabbak közül szalajtottak, úgyhogy megyünk előre töretlenül, amíg ki nem derül az igazság. Az igazság pedig most nem odaát, hanem ideát van.
Amikor 17 évvel ezelőtt először tanultam az OOP-ről, akkor ott a fejembe verték a konstruktor/destruktor jelentőségét és működését. A menedzselt környezetek világában (mint, amilyen a Java és a .NET) a konstruktor fogalma és jelentése maradt, de a destruktoré bizony jócskán megváltozott. A destruktor végrehajtási ideje hagyományos C++, Delphi, Pascal, stb. környezetekben előre meghatározható, NEM úgy, mint a menedzselt kódok esetén. Itt ugyanis sose tudhatjuk előre pontosan, hogy mikor fut le a destruktorban lévő kód, mivel a szemétgyűjtő folyamat kezdési ideje (ami egyébként a destruktort elindítaná) normál esetben előre nem határozható meg. Sok programozó azonban, aki felületesen ismeri a témát sajnos ezt nem tudja ezt, ami viszont később váratlan helyzeteket eredményezhet. Pont emiatt volt az, hogy a Microsoft a v2-es Program Language Specification kiadása után szakított destruktor kifejezéssel, és bevezette a finalizer fogalmat azért, hogy a programozó nehogy azt gondolja, ez ugyanaz, mint máshelyütt a destruktor (a "~" jelölés megmaradt). Na, de ez az írás nem is annyira a finalizer-ek belső életéről szól (akit ez érdekel az a hivatalos anyagokban bőven talál rá mintákat), hanem a szemétgyűjtésről.
Arról a szemétgyűjtésről, aminek egyébként az egyik feladata épp a programozó által készített finalizer metódusok (leánykori nevén: destruktorok) meghívása, és aminek főleg menedzselt kódból történő nem menedzselt kód hívásakor van igen nagy jelentősége.
Azt már tudjuk tehát, hogy a szemétgyűjtő algoritmus által végrehajtott finalizer metódus meghívás pontos ideje előre sajnos nem meghatározható. Azonban ezzel a válasszal mi nem vagyunk elégedettek. Akkor mégis, csak úgy megközelítőleg, vajon mikor hívódik meg ez a fránya finalizer metódus? A válasz az, hogy: azután, miután elindult a szemétgyűjtő folyamat. Frankó mi? Na ezzel aztán jól ki vagyok segítve. Akkor... akkor... izé... mikor indul el a szemétgyűjtő algoritmus?
A szemétgyűjtés többek között akkor indul el...
A CLR külön fenntart egy kitűntetett szálat az enyészet felé száguldó finalizer metódusok meghívására. Egy programozónak tudnia kell, hogy a finalizer metódusba csak a legfontosabb, minimális erőforrás felszabadítási műveleteket (pl.: adatbázis, fájl kapcsolatok lezárása, stb) tehet, azt is csak nagyon gyorsan és lehetőleg hibamentesen. Ugyanis amikor egy finalizer metódus végtelen ciklusba zuhan, akkor ez a kinevezett szál blokkolódik, és ettől kezdve elmarad az összes többi hasonló sorsra jutott objektum finalizer metódusának meghívása. Ami viszont azért rossz, mert amíg nem lehet tovább haladni, addig a többi objektum által foglalt memóriaterületet se lehet felszabadítani. Jó, de akkor mi a "B" terv?
Hát igen, nem lenne CLR a CLR, ha nem tartalékolna az ilyen helyzetekre egy "B" tervet. Egy finalizer metódusnak megközelítőleg 2 másodperce van arra, hogy befejezze a munkáját. Ha ezt a határidőt bármilyen oknál fogva nem képes tartani, akkor a CLR kegyetlenül kinyírja a folyamatot anélkül, hogy a többi finalizer metódus hívására sor került volna. Továbbá, ha az összes megszüntetendő objektum finalizer metódusainak az ügyködése meghaladja a 40 másodpercet, akkor a CLR úgyszintén a kegyetlenebb módszert választva elmeszeli a folyamatot örökre. Ez a gyilkolászás csak a finalizer metódusokon belüli, nem menedzselt erőforrások igénybe vételére van kellemetlen hatással, ugyanis a menedzselt objektumok és erőforrások esetén bármi is történik, a CLR teljesen szabályosan szünteti meg őket, felszabadítva ezzel a korábban általuk lefoglalt (menedzselt) memóraterületeket.
Amikor egy alkalmazás a new művelet hatására létrehoz egy új objektumot, akkor a menedzselt területen a kellő mennyiségű hely létrejön a számára. Ha ez az új objektum tartalmaz finalizer metódust, akkor az objektum mutatója bekerül egy úgynevezett "finalization list" nevű listába, még mielőtt az objektum konstruktora meghívódna. A finalization lista igazából a szemétgyűjtő folyamat által kezelt adatszerkezet. Minden eleme rámutat egy finalizer metódust tartalmazó objektumra, ami majd az objektum megszűnése előtt meghívódik. A következő ábrán nagyjából ezt az állapotot láthatjuk.
Tekintettel arra, hogy a finalizer folyamat a szemétgyűjtés lényeges és terjedelmes része, így erről bővebben majd legközelebb. Akkor viszont elmesélem ennek a műveletnek a pontos menetét, illetve azt is, hogy mi a bánat az a "freachable queue".
Folytatjuk... June 27 Computer History MuseumEgy kis történelem, itt-ott pontatlan fogalmazásokkal, de ez a lényegen mit sem változtat.
"...miközben a legtöbb cég még a szöveges [értsd: nem grafikus] operációs rendszerrel szarakodott, a Xerox mérnökei megalkották a grafikus felhasználói felületet és az egeret. Kár, hogy a vállalatvezetés elvetélt ötletnek találta mindkettőt, így nem zavarta a céget, amikor az Apple vezetője, Steve Jobs néhány emberével megjelent a Palo Alto-i fejlesztési központban és megnézte a gépet. A többi már történelem: a Xeroxnál látottak alapján elkészült minden idők egyik legfontosabb számítógépe, a Macintosh, amit áttanulmányozva viszont megírták minden idők egyik legfontosabb szoftverét, a Windowst."
Részletek itt. June 21 .NET szemétgyűjtés - 2.Az előző fejezetben megnéztük azt, amiről a tanfolyamok vagy alapszintű könyvek is beszélnek. Mint említettem, ennél azért mi szeretnénk tovább is jutni. Lökjük hát odébb a tudás szekerét, azaz végre már gyűjtögessünk egy kis szemetet...
A szemétgyűjtő algoritmus (garbage collector) tevékenysége abból áll, hogy megnézze van-e olyan objektum a menedzselt heap-en, vagy másképp mondva a menedzselt memóriaterületen, amelyet már nem használ senki. Ha van, akkor az általa lefoglalt helyet fel kell szabadítania, hogy visszakeülhessen a közösbe. Amennyiben ezután se marad elég szabad hely, akkor jön az OutOfMemoryException. Ugye milyen egyszerű az egész? Látszólag igen, valójában azonban majd meglátjuk, hogy mennyire nem.
Minden futó .NET alkalmazásnak (legalábbis szemértgyűjtő szempontból nézve) van egy úgynevezett gyökere (root). Ez egy olyan tárolási hely, amely memória mutatókat (pointer-eket) tartalmaz a referencia típusokra, objektumokra vonatkoztatva. Egy ilyen mutató vagy rámutat egy tényleges, élő objektumra, vagy pedig null az értéke (ami sehova se mutat). A gyökér elemek lehetnek a statikus mezők (egy adott típuson belül definiálva), vagy mondjuk a metódusok paraméterei, esetleg lokális változók, kivéve az érték típusokat.
Amikor a szemétgyűjtés elindul (majd később megnézzük azt is, hogy miért és mikor indul el), azzal a feltételezéssel él, hogy a menedzselt memóriaterületen lévő összes objektum szemét. Más szóval feltételezi, hogy a szálaknak fenntartott verem alapján (thread's stack) végignézve nincs semmilyen változó, nincs olyan CPU regiszter, sem statikus mező széles e világon, mely akár csak egyetlen egy objektumra is hivatkozna.
Nos, tehát ott tartottunk, hogy a szemétgyűjtés elindul és különféle feltételezésekkel él. Az első munkafázisa az úgynevezett jelölés (mark). A szálaknak fenntartott verem alapján végigsétál az összes gyökérelemen. Ha egy gyökérelem rámutat egy adott objektumra, akkor az adott objektum úgynevezett sync block index field-jén (ezt inkább nem fordítom le magyarra) átbillent egy bitet, és ezzel védettre jelöli az objektumot.
A fenti ábrán egy elképzelt helyzet látható. Az A,C,D és F objektumok gyökérelemeknek számítanak, mivel van rájuk hivatkozás, így tehát ezek az objektumok mindenképp jelölve lesznek. A szemétgyűjtőn belüli jelölő algoritmus (mivel a teljes, faszerkezet alakú gyökérlistán végigmegy) észleli, hogy a D objektum tartalmaz egy mezőt, ami hivatkozik a H objektumra, így a H is jelölve lesz. Az algoritmus egészen addig folytatja a munkáját, amíg az összes gyökérelemet (rekurzívan) végig nem vizsgálta. Amikor egy olyan elemmel találkozik, amelyet korábban (egy másik ágon) megjelölt, akkor teljesítmény optimalizálási okokból a megjelölt ágat már nem vizsgálja tovább, hiszen tudja, hogy korábban már járt arra.
Miután már minden ágon végig ment és minden "életre jelölést" végrehajtott, akkor ezután a menedzselt memóriaterület egy csomó jelölt és jelöletlen objektumot fog talmazni. A jelöltek még használatban vannak, a jelöletlenek pedig mehetnek a szemétbe (és már nem is érhetők el más objektumok számára).
Ezzel a szemétgyűjtő algoritmus be is fejezte az előkészítő fázist, és átlép a kövtkező, karbantartó fázisba, ami két részből áll. Az első részben a jelöletlen objektumok helyét azonnal felszabadítja (reclaim phase), és visszarakja a közösbe. A második, úgynevezett tömörítési részben (compact phase) pedig további vizsgálatokat végez arra nézve, hogy érdemes-e a felszabadított objektumok helyén lévő lyukakat eltűntetni, vagyis a meglévő, még élő objektumokat egymás mellé tömöríteni. Tömörítésre azért van szükség, hogy menedzselt memóriaterületen minél nagyobb, összefüggő, szabad hely legyen, hiszen egy következő helyfoglalásnál (amikor a new segítségével új objektumot hozunk létre) elképzelhető, hogy a korábban felszabadított objektumok helyén támadó kicsi lyukak egyikébe se lehetne beletuszkolni azt az új objektumot (mert annak leendő mérete miatt estleg nem férne bele).
Teljesítmény okokból az említett tömörítő algoritmus a túl kicsi szabad helyeket békén hagyja, és csak a méretesebb (érdemesebb) szabad helyeket tömöríti össze.
Természetesen, mivel a tömörítés során a még élő objektumokat át kell mozgatni egy adott memóriahelyről valahova máshova, így az összes rájuk mutató hivatkozás, legyen az egy objektum, lokális változó vagy CPU regsiszter érvénytelenné válik. Emiatt a szemétgyűjtőnek végig kell néznie az összes hivatkozást, és át kell állítania őket az objektumok tömörítés utáni, új memóriaterületére. Ráadásul, ha bármelyik objektum tartalmaz egy olyan mezőt, amelyik hivatkozott egy imént átmozgatott objektumra, akkor azokat is aktualizálni kell. Végül, miután helyre állt a rend, a NextObjPtr mutatóba bele kell gyömöszölni az éppen most kialakult, első szabad hely pozícióját (a fenti ábrán ez az állapot látható).
Hogy a szemétgyűjtés során milyen szál-kezelési, konkurrencia problémák vetődnek fel, milyen teljesítmény problémák adódnak, és hogy kapcsolódik mindehhez az objektumok megszüntetési (finalization) folyamata, vagy mik azok a generációs listák, na erről majd legközelebb.
Folytatjuk... June 16 C# 4.0 - Dynamic LookUpÚgy látom lesz egy új, Dynamic LookUp lehetőség valahol a C# 4.0 tájékán. Azok, akiknek az életét a más rendszerekkel történő együttműködés, és a késői kötések keserves kódolása töltik ki (pl.: interop with Win32 or COM objects), máris elkezdhetik csorogatni a nyálukat. Megpróbálom elmesélni a lényeget (remélem jól értettem az eredeti doksiban leírtakat).
A dologban az a trükk, hogy a „service” objektum példányon gyakorlatilag bármilyen nevű és paraméterű metódust vagy property-t meghívhatok. Tehát írhattam volna akár azt is, hogy service.HimbaPipics(„Ketymekütyü”, true); hiszen a fordító fordítási időben nem ellenőriz szinte semmit. Másképp mondva, az itt definiált késői, dinamikus kötés (adott objektum példányon adott paraméterekkel történő metódushívás) valójában annyira késői lesz, hogy annak a feloldása már csak futásidőben történhet meg. Ekkor viszont a CLR kegyetlenül leellenőrzi, hogy a meghívott, adott nevű függvény (jelen példában a „Do(…)” függvény) az itt megadott paraméterekkel és típussal létezik-e a „service” nevű objektumon értelmezve (vagyis a GetService() azt adott-e vissza, amit kellett volna). Ha nem létezik, vagy a típusa, esetleg a paramétere nem jó, akkor jön a futásidejű kivétel.
Metódusra, illetve property-re működik, és tipikusan az együttműködés (interoperability) támogatására szolgál. A programozó felelőssége, hogy olyan hívást állítson össze, ami helyes lesz. Ha elrontja, akkor az az első futáskor, azonnal kiderül (IntelliSense értelemszerűen nuku).
Anders Hejlsberg javaslata szerint csak indokolt esetben szabad használni, mert nyilvánvalóan lassabban működik, és több hibázási lehetőséget hordoz magában, mint a statikus típusok, melyek kötéseit már fordítási időben le lehet ellenőrizni. Egy valós példa: C# 4.0, Dynamic Programming and JSON
Az efféle dinamikus programozás és nyelvek témája nem egyedüli. A Sun-nak is van egy 2008-ban indított, és hasonló funkcionalitást megcélzó kísérleti projektje: „Da Vinci Machine” néven. Ha elkészül, akkor a Java nyelvbe is beépül a dinamikus metódushívási és típusellenőrzési módszer, azaz ők is képesek lesznek ugyanígy futásidőben szabadon cserélgetni az osztályokat, metódusokat. A tervek szerint ez a Java 7-ben fog elsőként debütálni (viszont egy működő prototípus az OpenJDK-ban már most is megtekinthető). June 14 .NET szemétgyűjtés - 1.Bizonyára mindegyikünkkel előfordult már, hogy olvasott egy vagy több .NET-ről szóló könyvet, illetve részt vett ugyanilyen tanfolyamokon. Akkor az első órák egyikén hamar megtudhatta, hogy a menedzselt kódokat előállító technológiákban (mint amilyen a Java és a.NET is) az egyes változók, objektumok, tárterületek memóriabeli helyfoglalása rém egyszerű dolog. Deklarálom amit kell, használom amíg szükséges, aztán utána jól magára hagyom (kivéve akkor, ha nem menedzselt erőforrásról van szó).
A tanfolyami előadók, vagy könyv szerzők mint az indiai szerzetes a mantrát úgy mondogatják, hogy "sebaj, majd jön a szemétgyűjtő algoritmus (garbage collection) és jól etakarítja a feleslegessé vált, vagy nem használt objektumokat". Jó-jó, de mi is az a szemétgyűjtő? És... és mikor indul el? Izé..., ha meg már elindult, akkor hogyan teszi a dolgát? Mind-mind megannyi kérdés, melyekre általában sose kapunk kielégítő választ (vagy csak nem merjük megkérdezni). Hejj, magam is számtalanszor voltam már így egészen addig, amíg nem vettem a fáradságot és jó alaposan utána nem néztem a dolgoknak. Reszkessetek szemetek, most aztán jól kitárgyalunk benneteket!
Először is kezdjük azzal, amiről már itt-ott megemlékeztem, nevezetesen hogy a .NET objektumokat (a változók is azok) két helyen tárolódnak. Az érték típusok a vermen (eredeti nevén stack-en), a referencia típusok pedig a menedzselt halmon (vagy eredeti nevén managed heap-en). A verem karbantartása automatikus, ahogy megszűnik az adott változó hatóköre, azonnal felszabadul a helye. A referencia típusoknak fenntartott menedzselt heap-en azonban más szelek fújnak, mivel ott a szemétgyűjtő algoritmus a király. No de ne siessünk ennyire előre, inkább ugorjunk vissza egy picit az alapokhoz. Ahhoz, hogy a szemétgyűjtő uraság elindulhasson a gyűjtögetési körútjára, előbb szükségesek maguk az eltakarítandó objektumok.
Mikor egy .NET folyamat létrejön, akkor a CLR (Common Language Runtime) lefoglal egy több részből álló, folyamatos memóriaterületet (menaged heap + azon belül kölönféle rész területek), majd hozzárendel egy mutatót NextObjPtr néven. Ez a mutató mindig azt jelzi hol van a legközelebbi szabad hely.
Egy objektum létrehozásakor, pl.: List nevek = new List(); a CLR az alábbi lépéseket hajtja végre:
1. Kiszámolja a létrehozandó objektum helyigényét bájtokban (az összes ős osztályban meglévő
helyfoglalási igényekkel együtt). 2. Hozzáadja azokat a bájtokat, melyek az adott objektum adminisztrálása miatt szükségesek
(ez 32-bites környezetben egyszer 4 bájt + objektumonként újabb 8 bájt, illetve 64 bites környezetben egyszer 8 bájt + objektumonként 16 bájt). 3. Leellenőrzi, hogy az igényelt bájtoknak megfelelő szabad hely vajon rendelkezésre áll-e. Ha igen,
akkor elhelyezi ott az objektumot (üresen), majd meghívja az új objektum konstruktorát és átadja neki a NextObjPtr mutató aktuális értékét (ami ekkor bekerül a this változóba). Végül odébb állítja a már említett, következő szabad helyre mutató NextObjPtr értékét. A fenti ábrán az A, B, C mind egy-egy objektumot jelképeznek a managed heap-en.
Amikor a CLR az objektum helyfoglalása során úgy érzi, hogy igazából már nincs elegendő hely a managed heap egy adott rész területén, akkor megrázza magát, és felébreszti azt a szálat (thread), amelyik a szemétgyűjtő algoritmus végrehajtásáért, azaz a garbage collection-ért felel. A szemétgyűjtőnek az a dolga, hogy megnézze mely objektumokat használnak még, és melyeket hagytak már magukra. Amikor ezzel végez, akkor az elárvult objektumokat eltakarítja az útból (kipucolja a memóriát), ezáltal szabaddá téve az általuk korábban lefoglalt területeket. Ha a takarítás után se marad elég szabad memória, akkor bizony jön a végzet, méghozzá OutOfMemoryException formájában (ami mint tudjuk az aktuális folyamat leállásához vezet).
Ez volt a bemelegítés. Nagyon leegyszerűsítve az alapszintű könyvek és tanfolyamok során általában eddig lehet eljutni, és sokan ezzel is elégedettek. Azonban ennyi információ láttán én egy cseppet se lennék megelégedve (feltételezem, hogy a kedves olvasó se). Ez az írás nem azért jött létre, hogy csak úgy bemelegítgessünk, vagy hogy a felszínt kapargassuk. Akármilyen programozóval reggel a Moszkva tér környékén is találkozhatok, de olyan programozóval, aki tisztában van azzal mi, hogyan működik, na olyannal már hajólámpással a kezemben is csak nagyon ritkán akadnék.
Folytatjuk... June 08 Hamarosan...Nemsokára belekukkantunk a .NET szemétgyűjtő algoritmusába, illetve annak működésébe - magyarul. Sokak szemében ez egy meglehetősen misztikus téma, és amennyire én tudom itthon elég kevés írás jelent meg róla, pedig fontos. Az általam eddig megtapasztalt .NET tanfolyamokon ilyenről nem nagyon beszélnek, holott igény lenne rá. Szerintem.
A kedves olvasónak már csak pár napot kell aludni (nekem meg addig elég erőt gyűjteni)... Visual Studio - Mono ToolsVisal Studio környezetből Mono fejlesztés támogatás:
Részletek itt. June 06 BeelőzöttAz újdonság izgalma. Vagy mégsem?
"A Microsoft keresőjének bing néven kiadott legfrissebb inkarnációja csupán néhány napja mutatkozott be, de máris megelőzte a legrégebbi motorost, a Yahoo-t."
Részletek itt. June 05 KétszázezerNem annyira szakmai téma következik, de talán így az EP-választások előszobájában a kedves olvasók is jobban elnézik nekem ezt a kis kitérőt.
Az az igazság, hogy egyáltalán nem kedvelem a mai, magyarországi politikai pártokat, de hát szavazni, választani azért mégiscsak kell. Aki nem így tesz, annak vajmi kevés joga van akár egy szóval is illetni őket (szerintem egyébként meg arra kell, szavazni, aki a legkevesebb bajt okozza vagy okozta és kész).
Azonban van egy párt, ami mégis rosszabb, kártékonyabb, elvtelenebb mindegyiknél. Az utóbbi 15 éves tevékenységüket magamban nemes egyszerűséggel csak "verbális holokausztnak" hívom. Ha nem vigyázunk, előb-utóbb azt teszik velünk (nem ténylegesen, csak szavakkal), amit a nácik tettek több millió embertársunkkal. Jó lesz hát vigyázni!
A következő írást pár perce találtam teljesen véletlenül, de annyira megtetszett, hogy vettem a bátorságot és azonnal közzé tettem.
"Ti ...toleráltok minden másságot, elfogadjátok a szerencsétlen sorsú, bűnözővé válni kényszerült cigány mentségeit, megértitek a melegek problémáit, magatokénak valljátok néhány tucat paranoiás zsidó félelmeit, csak egyet nem fogadtok el: ha valaki másképp gondolkozik mint ti. Éppen olyan megvetendőnek tartjátok a vallásos ember hitét, mint a szélsőjobboldal a melegek életmódját, ugyanúgy lenézitek a történelmi hagyományokat, mint amazok a ti ,,korszerű" eszméiteket, s éppúgy nem tudtok mit kezdeni a nemzettel, mint ahogyan ők nem tudnak mihez fogni a holokauszttal."
Részletek itt.
"Ti ...azt hirdetitek, hogy a globalizáció - mint hajdan a kommunizmus, vagy még korábban az ész uralma, esetleg még előbb a keresztény eszmék diadala - majd automatikusan megoldja az emberiség problémáit. Pedig dehogy oldja meg! Ép ellenkezőleg: eltöröl egy csomó problémát, és helyettük új kihívásokat állít elénk. És mivel ezt minden ember tudja, alig hihető, hogy éppen ti ne tudnátok. Ha viszont tudjátok, akkor nem a kiszolgáltatottak, az üldözöttek, a globalizáció veszteseinek oldalán álltok, hanem éppen, hogy az ellenoldalon. Kirekesztésről beszéltek, miközben nincs nagyobb kirekesztettség annál, mint amit a multinacionális tőke, a politikai elit, vagy éppen a média produkálni tud.
[..]
Ti nem vettétek észre, hogy ma Magyarországon és Európában nem azok fenyegetik az európai értékvilágot, a szabadság eszméit, akikkel szemben álltok, hanem éppen, hogy azok, akiket meg akartok védelmezni."
Uhh, ez most nagyot ütött. Szerintem.
Részletek itt.
Az igazság pedig (az én értékrendem szerint is) ebben a mondatban lakozik:
"Nem értek egyet azzal, amit mond, de halálig védelmezem a jogát, hogy elmondhassa."
- Voltaire - Top 10 IT-trendJajj, ez az egyik "kedvenc" témám.
"...számos vállalat komolyan fontolóra veszi majd, hogy nyílt forráskódú szoftverekkel oldja meg a feladatait. Már most is számos alternatív ERP program (Compiere, Openbravo, ADempiere stb.) érhető el az interneten, amelyek versenyképes áron kínálnak hasonló funkciókat, mint a nagyok."
Hát én ezeken mindig olyan jót derülök, hogy csak na. Komolytalan banda. A nyílt forráskódú ERP rendszereket a nagyokéval összehasonlítani kb. olyan, mint egy talicskát egy Liebherr dömperrel. Ez van fiúk.
"...az ár és az érték a piacon méretik meg. Senki se higgye azt, hogy a nyílt forráskódú rendszerek tized-, századáron ugyanazt tudják, mint a nagy szoftvergyárak termékei. Természetesen nem kételkedem az emberi kreativitásban, de azért anélkül, hogy lebecsülném a nyílt kódú szoftvereket, meg kell kérdeznem, hol jelenik meg a felelősség és a garancia, az üzleti biztonság egy olyan szoftverben, amelyet le lehet tölteni az internetről? Egy programozó nem attól zseniális, hogy magában írja-e a kódokat vagy egy szoftvergyárban. Úgy gondolom, hogy a jelentős alkalmazásfejlesztő szoftvergyárak létükkel és üzleti konstrukcióikkal kínálják a biztonságot, a felelősséget és a garanciát."
Bizony mondom jól löki a fickó, és ez még csak a jéghegy csúcsa. Erről már én is írtam régen, így most nem ismétem magam (pedig már majdnem rámozdult a billentyűzetem, de hát... nem, nem, inkább mégse...).
Részletek itt. May 31 BingA múlt héten hallottam először róla. Akkor azt gondoltam, hogy "Na, már megint egy újabb keresőmotor, ami szép lassan elvérez a nagy Google ősellenség hasonló, már-már ikonná vált keresőjével szemben." Ez az érzés egészen addig tartott, amíg meg nem néztem egy Channel9 videót erről az új keresőszoftveréről. Bing!
A neve olyan, amilyen a hatása lesz. Most először érzem azt, hogy igen, ez valami tényleg más, mint ami idáig volt. Nem csak egyszerű keresőmotorról van szó, annál sokkal-sokkal több. A keresés mellett csoportosítást végez és segít a döntéshozatalban. Rengeteg olyan extra funkció meg lehetőség, amit most elmondani nem lehet, és amire nekem korábban igencsak szükségem lett volna.
Ha jól tudom június 2-től indul a nyilvános béta teszt, azt hiszem csak az USA-n belül (http://www.bing.com), addig is bemutató video itt.
Ja, és valaki tudja mit jelent a Bing? Egyesek szerint ezt: Bing Is Not Google (csak vicc volt). May 24 Regressziós tesz? Mi az?Nagyjából 11 éve írta egy ma ismert és istenített ember ezt velős gondolatot: "regression testing"? What's that? If it compiles, it is good, if it boots up it is perfect.
Ha ma valaki egy komoly fejlesztőcégnél, mondjuk az állásinterjún kimondaná ezeket a szavakat (így egymás után, ebben a sorrendben), vajon alkalmaznák-e? Azért se árulom el ki volt, mert az árulkodás csúnya dolog. Eredeti levél itt. May 23 VisualStudio és .NET Framework készítési folyamataLáttam egy video-t az MSDN Channel9-en, ami a VisualStudio és .NET Framework, úgynevezett "build" folyamatáról szól. Sokat nem értettem belőle (sajnos még 20 év után is nagyon nehezen fogom fel az angolt akkor, ha azt egy eredeti anyanyelvű beszéli), de remek kis képek meg ábrák vannak benne.
Video itt. Windows API .NET alattA redmondi boszorkánykonyhán bizony szorgos munka folyik. A szakácsok vígan dalolnak naphosszat, csak úgy, ahogy egyébként is szoktak. De csitt! Hallgassunk bele, miről is szól e csevely...
Ez az egyik kedvenc szövegrészletem, amit fiatalabb koromban kívülről fújtam, és nem csak az eredeti mű, hanem annak egy feldolgozása miatt is. No,de nem pont erről akartam szólni, csak picit passzolt a témába. Talán. Vagy nem? Mindegy.
Hogy mit is főznek a redmondi boszorkányok? Hát, úgy néz ki, hogy egy Windows 7 Managed Code API nevű ételt. Ennek lényege, hogy a Windows 7 menedzselt API segítségével is elérhető lesz .NET-ből. Akinek ez szükséges, annak a jövőben már nem kell C/C++ vagy COM+ kódot reszelnie menedzselt kódból, hanem maradhat a jól megszokott környezetében.
Hevenyészve összefoglalom a tartalmát:
May 16 Pályázunk v2.0Tavaly szempetmberben jeleztem, hogy több K+F pályázatot is beadtunk. Ebből az egyik információbiztonsághoz és adatbányászathoz kapcsolódó szerencsére nyert is (a projekt összeg több, mint 100 mFt). A másik egy vadonat új, kifejezetten ügyviteli ERP rendszerek fejlesztését támogató, többrétegű, komponens alapú architektúra szerinti, domain modellel, SOA-val, deklaratív programozással fűszerezett technológia, fejlesztési keretrendszer és szoftvertermék lett volna (projekt érték több, mint 900 mFt).
Sajnos ez utóbbit nem nyertük meg, pedig akkor úgy éreztem, hogy életem talán egyik legjobb pályázati anyagát írtam (már, ami a műszaki és szoftveres részét illeti a dolognak). A pályázat eredménytelenségének, illetve az elutasításának a hivatalos indoka az volt, hogy "nincs elég újdonságtartalma". Az tudni kell, hogy az újdonságtartalom volt az egyik legmagasabb pontszámot jelentő értékelési szempont (mivel EU támogatásos pályázat volt), ami azt jelenti hogy a K+F végeredménye nem csak Magyarországon, hanem azon kívül is értékesíthető kell, hogy legyen.
Nos, én szó szerint ennek a szellemében írtam a műszak-technológiai fejezeteket. Bőszen igyekeztem megtalálni és kihangsúlyozni a fejlesztés újdonságtartalmát, kerestem meglévő példákat mind a zárt, mind pedig a nyílt forráskódú világból (nincs túl sok), és összehasonlítottam velük az általam kigondolt megoldást. Természetesen az összehasonlítás eredménye az volt, hogy számos területen (műszaki fejlettség, de főleg programozói hatékonyságnövelés terén) a mi megoldásunk sokkal több újdonságot nyújtott volna a meglévőeknél. Ez most szerénytelenségnek tűnik, de az iménti magabiztosságom oka jószerivel az, hogy ezer éve ERP rendszer fejlesztéset, programozást (ahh..., na és persze programozókat), megoldásokat terelgetek jobbról-balra, aminek köszönhetően rengeteg tapasztalatot sikerült begyűjtenem.
A pályázati anyag műszaki részében volt minden, ami szem-szájnak ingere. Így pl.: a fentebb már említett divatirányzatokon (buzzword-ökön) kívül objektum-reláció, alkalmazás szolgáltatás busz, technikai és emberi workflow menedzsment, dokumentumkezelés, térinformatika és grafika, elektronikus aláírások garmada, e-számla kezelés, programozói teljesítménynövelést támogató szoftverkomponensek és módszerek, szóval minden, amitől korszerűen, hatékonyan, gazdaságosan (értsd: olcsón), kevés emberi erőforrás igénybevételével lehet nyereségesen "termelni". A termelés alatt izt most az ERP rendszerek fejlesztését, tesztelést, üzembehelyezését, utólagos módosíthatóságát és karbantarthatóságát, illetve a hatékony üzemeltetést kell érteni.
Visszatérve az elutasítás kevés újdonságtartalmára vonatkozó indokra úgy gondoltam, hogy ez butaság. Igaz vagy sem, ettől azért még elszontyolodtam rendesen, hiszen nem ehhez voltam szokva. Hanem ahhoz, hogy a megírt műszaki tartalom legtöbbször sikeresen hozzájárult a pályázati projektjeink elnyeréséhez (ez olyan 80%-os arányt jelentett a nyert pályázatok javára). Nem értettem azt, hogy miben hibáztam és vadul kerestem a tévedésem okát, hiszen a jövőre nézve, ebből sokat lehetne tanulni. Az elkövetett hibák megismerése, elemzése segített nekem mindig is abban, hogy kétszer ugyanabba a csapdába ne lépjek bele. Sajnos ezesetben nem lett meg az ok, leglábbis akkor (tavaly decemberben) még azt hittem.
Aztán teltek-múltak a napok, hetek, hónapok, amikor egyszer csak megjött a várva-várt infó, igaz hogy kertek alatt, rejtett, nem hivatalos csatornákon. Az igazi, valós probléma a mi első pályázatunkkal nem az újdonságtartalmának hiánya volt (ahogy sejtettem), hanem az, hogy valaki, vagy valakik nem szerették volna, ha mi nyerünk, főleg nem, hogy ennyit. Magyarul, "barátilag" elkaszáltak (ehhez azért tudni kell, hogy a cégvezetőnk intenzíven forog felsőbb informatikai és politikai körökben, úgyhogy akár szertezhetett is pár ellenfelet magának). Nincs rá bizonyítékom, hogy így történt, de erősen hajlok rá, hogy tényleg. Nem baj, legalább tudom, hogy nem hibáztam nagyot, és hogy jó volt az irány, amit akkoriban megálmodtam.
Azóta kiírtak egy új pályázatot (másik intézmény, másik keretből), ami jóval szerénybb, mindössze 300 mFt. értékű projekt keretet jelentene nekünk, de sebaj, legalább újra megpróbáltuk. Az új anyag egy hónapja készült el és lényegében a korábbi, szeptemberben beadott, de végül sikertelen pályázat egy módosított, csökkentett változata lett. Ez jóval szerényebb álmokat tartalmaz és kisebb költséggel valósítható meg, de technológiai megoldásásait és hatékonyságát tekintve majdnem annyira jó lenne, mint amilyen az eredeti elképzelés volt.
Most tehát nincs más dolgunk, mint várni az eredményre. Jajj, úgy izgulok... Iteráció teljesítménynövelésA múltkori témák adták az ötletet arra nézve, hogy néhány további, teljestménynövelésre buzdító javaslatot tegyek, de most inkább csak az iterációkra (ciklusokra) vonatkoztatva.
A korábbi példákhoz nagyon hasonló eset az, amikor az egymásba ágyazott ciklusok önmagában álló teljesítményét vizsgáljuk. Az utasítások működési jellemzőjéből adódóan, a belső ciklus minden egyes iterációjának alkalmával a ciklusváltozót inicializálni kell, az összehasonlító logikai kifejezést értékelni kell, valamint ciklusváltozót is növelni kell.
A fenti példában kívül van a több, belül pedig a kevesebb iterációt végrehajtó ciklus. Ezesetben a ciklusváltozók menedzselése egészen pontosan 100 + (100 * 5) = 600 alkalommal hajtódik végre, ami kiélezett helyzetben tetemes időt vehet igénybe. Ilyenkor nincs más teendő, mint a külső, többször iteráló, illetve a belső, kevesebbszer iteráló ciklust egyszerűen felcserélni egymással.
A javított változatban a ciklusváltozók menedzselése 5 + (5 * 100) = 505 alkalommal hajtódik végre, ami a 16%-kal kevesebb feladatot ró a működtető környezetre (ez utóbbi példa javára). Természetesen .NET esetén ez a példa picit erőltetett, mivel a JIT fordító számos optimalizálást már előre végre tud hajtani. A végső megvalósítás előtt ezesetben is érdemes méréseket végezni.
Szintén ciklusok esetén jellemző az, amikor mondjuk egy metóduson belül lévő ciklusban végre kell hajtani bizonyos műveleteket, aztán később ugyanilyen cikluson belül egy másik műveletet. Ezesetben érdemes megvizsgálni annak a lehetőségét, hogy a két azonos feltételekkel rendelkező ciklust nem lehetne-e összevonni (ezek az úgynevezett „jamming” vagy „fusion” műveletek).
A fenti megoldás helyett, amennyiben ez lehetséges, legyen inkább összevonás…
A ciklusokon belüli összetett műveletek mértékét a lehető legkisebbre kell venni. Ez azt jelenti, hogy minden idő és erőforrásigényes utasítás esetén meg kell vizsgálni, hogy nem lehet-e azt kiemelni a cikluson kívülre.
Ha a „rates” objektum „CalculateDiscounts()” metódusa mondjuk adatbázis, vagy más erőforrásigényes műveletet hajt végre, aminek a végeredménye az adott időben ugyanaz, akkor érdemes kiemelni azért, hogy csak egyszer hajtódjon végre (tudom, hogy ezek elemi dolgok, de hátha olyasvalaki is olvassa, aki csak most kezdi a programozói pályáját). Tudom-tudom... ezek primitív dolgok, de akkor is.
Amennyiben egy cikluson belül már nincs értelme a további iterációnak, akkor gondoskodni kell az iteráció azonnali, vagy legalábbis a lehető leghamarabb történő befejezéséről. Minden további felesleges műveletet teljesítmény és erőforrás gazdálkodási okokból kerülni kell. Élet a Windows utánTaláltam az indexen egy cikket a témáról. Jó része már ismert, de azért egy-két mondat mégis újdonság volt.
"A cég [mármint a Microsoft] nyitottabb, mint legtöbb konkurense, elképzelhetetlen lenne például, hogy az Apple kötetlen beszélgetésre hívja a magyar bloggereket, újságírókat. Márpedig a Microsoft egyre gyakrabban szervez ilyesmit...
[...]
A Vista hardverigénye mellett problémát jelentett, hogy túl sok komponense épült egymásra, ami nem lett volna baj, ha ezek mind elkészülnek. A WinFS fájlrendszert, és számos szolgáltatást azonban menet közben kukáztak, így az operációs rendszer végső verziója közel sem lett annyira használható, mint azt eredetileg tervezték.
[...]
Még csiszolni kell a Windows 7-et, de Budai szerint a nemrég megjelent RC verzió olyan jól sikerült, hogy ha nem jön közbe egy váratlan jogi döntés, a végleges verzió alig különbözik majd tőle.
[...]
Sinofsky [az üzletág vezetője] felügyelete alatt születő programok kiadása még sosem késett, így borítékolható volt, hogy nem nyúlik majd olyan hosszúra a fejlesztés, mint a Vistánál.
[...]
A fejlesztés [az új Midori rendszer fejlesztése] során mindent a nulláról kezdtek, az ablakkezeléstől a processzorkezelésig mindent új oldalról közelítenek meg, ami Budai szerint gyönyörű megoldásokhoz vezet, de ennél többet nem mondhatott a rendszerről. Az viszont kiderült, hogy a Midori olyan a Microsoftnak, mint a NASA az emberiségnek: mindenféle hasznos fejlesztéssel gazdagítja a széles körben elterjedt technológiákat..."
Részletek itt. May 12 Szoftvergarancia?Ajajj, végünk lesz, ha lesz.
"A szigorú európai uniós szabályozásoknak köszönhetően a tagállamok területén legálisan nem lehet forgalomba hozni olyan termékeket, melyekre a gyártó nem vállal - értéktől függően, törvényben meghatározott ideig - garanciát.
[...]
...aggályos a programozói felelősséghez ragaszkodni egy hardveresen heterogén környezetben, ahol gyakran nem is a kóderek munkája miatt történnek hibák a szoftverek futása közben."
Nagyjából 16 év alatt, az ügyfeleinknél dolgozó felhasználók által jelzett programhibák legalább 30%-a környezeti (operációs rendszer, adatbázis-kezelő), illetve hardver vagy hálózati hibák miatt következett be. Olyan is volt, hogy a hiba bár a mi rendszerünkben jelentkezett, közvetve mégis egy másik fejlesztőcsapat szoftvere okozta. Az is előfordult, hogy egy hibásan működő vírusvizsgáló, képernyőkímélő mellékhatása jelentkezett - nálunk. Őrülten nehéz azt bebizonyítani (főleg 2-300 km távolságból és néhány nap elteltével), hogy egy összetett környezetben az "X" vagy "Y" rendszere, "Z" hardvere, vagy "Q" tevékenysége okozta a bajt. Főleg akkor nehéz, amikor a ma olyannyira népszerű "szállítótól való függetlenség" jegyében egyszerre több partnercég is szerepel a képletben (régen mindenkinek sokkal egyszerűbb volt, mivel egy projekten rendszerint csak egy beszállító ügyködött, és ő adta a hardvert, meg a szoftvert, meg még talán a sört is az ebéd mellé).
Aki hivatásszerűen, nap-mint nap szoftvert fejleszt, küzd az elemekkel, problémákat kezel, no meg vitatkozik az ügyfelekkel, tudja mit jelent ez. Az EU-bürokratáknak fogalmuk sincs erről. Remélem sose lesz belőle semmi.
Részletek itt. May 10 Tömbölés - 2.Egy picit még elidőzünk a múlt héten felvetett téma mellett. Nyilvánvaló, hogy a tömbök, főleg ha ciklusokon belül használjuk őket néha igen komoly teljesítményproblémát jelenthetnek, melyek egy része magától értetődő, más része rejtett. Az alábbiakban egy-két jellemző példát lehet majd látni erre.
Tételezzük fel, hogy egy táblázatot alkotó "jagged" tömb minden egyes sora kb. 4000 bájt méretű (figyelem, a méret a lényeg!). Ha ez a táblázat túl sok sorból áll, akkor minden esetben, amikor egy eltérő sor adataihoz hozzá szeretnénk férni, akkor a tábla/tömb és az állandó memória lapméretek miatt (mondjuk 4 KB) az operációs rendszernek úgynevezett memória-lapváltást kell végrehajtania. Másképp mondva hozzá kell férnie a tömbelem másik lapon lévő adataihoz. Vagyis a lenti példa szerint a belső ciklus miatt, minden tömbelemhez való hozzáférés újabb és újabb plusz memóriaműveletet (a már említett lapváltást) generál.
Példa:
A fenti példa szerint tehát MAX_COLUMNS * MAX_ROWS darab memória-lapváltás történik, melyet mindenképp jó lenne kiküszöbölni, és erre az alábbi lehetőség áll a rendelkezésünkre.
Egyszerűen megcserélődött a két ciklus, vagyis ami eddig kívül volt az belül lesz és fordítva. Ebben az esetben is lesz memória-lapváltás, de már csak MAX_ROWS és nem MAX_COLUMNS * MAX_ROWS esetben. A teljesítménynövekedés feladattól és környezettől függően változhat, de bizonyos esetben, pl.: a második példa ciklusa akár 1000x előbb is befejeződhet, mint az első.
Ha már a tömböknél tartunk... Az alábbi példa igaz, hogy a kód olvashatóságát rontja egy kissé, de teljesítmény szempontból, bizonyos helyzetekben rendkívül hatékony tud lenni (ez az úgynevezett „unrolling” művelet).
Példa:
Ez egy egyszerű ciklus volt, ami egy „a” numerikus tömböt feltöltött az „i” index értékével. A fenti megoldás helyett, amennyiben lehetséges (nyilván nem mindig), legyen inkább egy másik megoldás…
A lényeg az, hogy a tömbön belül nem egyesével, hanem fura módon kettesével léptetjük az „i” indexet, és minden iterációnál kitöltjük az aktuális, valamint az aktuális i + 1 tömbelemet (vagy más feladat esetén végrehajtunk valami más tömbműveletet). Páratlan számú iteráció esetén az utolsó elem kitöltésére csak a ciklus után, külön kerülhet sor. A teljesítménynövekedés környezettől függően változhat, ezért az alkalmazása előtt méréseket kell végezni.
Ahol a teljesítmény különösen kritikus és fontos, ott a .NET menedzselt kód által nyújtott plusz szolgáltatások kikerülhetők, azaz elérhető, hogy a tömbök adata ne a referencia típusokra jellemző menedzselt halmon (heap), hanem az annál jóval gyorsabban hozzáférhető, és a szemégyűjtő Garbage Collector által nem inzultált vermen (stack) tárolódjon.
Az imént említett tömbhasználat felturbozásához a stackalloc nevű C# utasítást kell használni (fordításkor a /unsafe opció szükséges). Ez a módszer csak egy dimenziós, 0 bázisú, érték típusú elemeket tartalmazó tömböknél működhet. Amikor a stackalloc használatban van, akkor a memória blokkok eléréséhez úgynevezett unsafe pointer-t alkalmaz a rendszer. Ezek a memóriaterületek az adott hatókör (pl.: egy metódus return utasítása) után automatikusan felszabadulnak.
Példa:
Minden téma végén megjegyzem, hogy ezeket a megoldások és példák természetesen nem az én fejemben születtek. Ahhoz, hogy az ember hozzájusson a fenti okosságokhoz, egyszerűen csak olvasni kell, méghozzás sokat. "Mert olvasni nem gyíkság..." May 03 Tömbölés - 1.Elismerem, picit fura a cím, tegyük tisztába hát. A "Tömbölés" szót ebben az értelemben én találtam ki, és magyarról magyarra fordítva valami "tömbök kezelésének érdekességei programozási nyelvekben"-féleséget kell érteni. Az ötletet, hogy egy picit foglalkozzunk a témával két remek könyv ihlette. Ezek kapcsán gondoltam úgy, hogy bizonyos jelenségek és problémák, melyeket majd leírok (és amelyek nem az én agyszüleményeim), minden programozó számára hasznosak lehetnek a jövőben.
Elöljáróban annyit, hogy nem igazán szeretem a tömböket. A múltban, amikor még Fortran, Basic vagy Turbo Pascal nyelvek töltötték ki a napjaimat, más lehetőség hiányában én is intenzív tömbhasználó voltam, aztán kb. 15 éve, amikor az első igazi relációs adatbázis közelébe kerültem, teljesen elbűvölt az itt fellelhető adatszerkezetek, adattárolási (meg egyéb) lehetőségek tárháza. Azóta kerülöm a tömböket bár elismerem, néha napján bizony jól jönnek, sok esetben meg nem is lehet meglenni nélkülük.
A következőkben elsősorban az általam jobban ismert .NET alapon létező, C# nyelven készített tömbökről beszélek (a többi platformon vagy nyelven elérhető tömöktől ezúton is bocsánatot kérek).
Minden tömb a System.Array absztrakt osztályból származik, melynek direkt őse a System.Object. Ez egyben azt is jelenti, hogy tömb referencia típus, és így az úgynevezett menedzselt halom (managed heap) területen foglal helyet. Alapvetően háromféle tömb létezik: az egy dimenziós (vektor), a jöbb dimenziós (mátrix, kocka, stb), illetve a szabálytalan (tömböket tartalmazó tömb, vagy hivatalos nevén "jagged") tömb. Minden tömb deklaráció egy hivatkozás (referencia) a hozzá tartozó, adott típusú adatokra. A tömbök elemeire való hivatkozás (tömb index) alapján történik, amely alapértelmezés szerint 0-tól indul, de persze ha úri kedvünk úgy tartja, ettől elétérő is lehet.
Példák:
A tömböket automatikusan vagy kényszerítve, de át lehet alakítani egyik típusból a másikba.
Példák:
Az Array típusnak számos jellemzője (property) és metódusa van, melyek jól használhatók a munka közben.
Példa:
Ja igen, ez tényleg olyan unalmas, hogy már magam is vattát köpök. Rendben, akkor elemi iskolai magyarázatokból ennyi elég is volt. Nem áll szándékomban programozói tanfolyamot indítani, úgyhogy lépjünk tovább az érdekesebb, főleg nem várt tejesítményproblémákat felvető részek felé.
Az Array.Copy() metódus nem az eszeveszett sebességéről híres, épp ezért megfontolandó, hogy nem érdemesebb-e helyette időnként a System.Buffer.BlockCopy() metódusát használni. Ez, a benne alkalmazott bájt-eltolási módszer miatt lényegesen gyorsabb az előzőleg említett "kollégájánál", azonban csak az egyszerű (primitive) típusú tömbök másolását támogatja, és nem ajánlja fel ugynazon konverziós lehetőségeket, minta sima Array.Copy(). Elsősorban bájt vagy karaktersorozatokat tartalmazó tömbökhöz célszerű használni. A BlockCopy()-ról részletek itt olvashatók.
Amennyiben igazán biztonságos másolásra vágyunk, akkor a System.Array.ConstrainedCopy() metódus a mi emberünk. Ez ugyanis garantálja, hogy a másolási művelet teljesen szabályos legyen, ellenkező esetben kivételt fog dobni. Sajnos a forrás tömb csak ugyanolyan típusú lehet (vagy ugyanannak a leszármazottja), mint a célként meghatározott tömb. A ConstrainedCopy()-ról részletek itt olvashatók.
A tömbök mindegyike automatikusan (implicit módon) megvalósítja az IEnumerable, ICollection, és IList interfészeket, a velük járó lehetőségekkel együtt. Azonban a nevezett interfészek generikus változatainak a megvalósítását a CLR (Common Language Runtime) csapat nem hajtotta végre, hivatalosan elsősorban a több dimenziós és nem 0 kezdetű tömbök kellemetlenségei miatt. Ugyanakkor egy kis trükkel futás közben mégiscsak belecsempészték a generikus interfészeket, de ezek elmagyarázása sajnos túl sokáig tartana, úgyhogy egyelőre legyen elég annyi, hogy a fentebb említett interfészek generikus változatai nincsenek ugyan, de mégis vannak.
Egy egydimenziós, 0 kezdetű (0 bázisú, vagy 0 kezdő indexet tartalmazó) tömb kezelése lényegesen gyorsabb a nem 0 bázisú és/vagy többdimenziós tömbökhöz képest. Ennek egyik oka az, hogy az egydimenziós, 0 bázisú tömbre külön IL (Intermediate Language) utasítások vannak, úgy mint: newarr, idelem, idelema, stelem. Ezek felhasználásával a JIT fordító képes remekül optimalizálni a műveleteket. Pl.: a 0 bázisú, egydimenziós tömbök eltolását (memóriabeli elhelyezkedésük címét) egyszerű műveletekkel képes kiszámolni, míg a nem 0 bázisú tömböknél viszont különféle osztásokat is kell még végeznie. A másik ok az, hogy a 0 bázisú tömböknél a JIT fordító képes az index tartomány ellenőrzést előre, pl.: még a ciklus végrehajtása előtt elvégezni. Mondanom se kell, hogy ezzel bizony majdhogynem fénysebességre gyorsulhat a művelet.
Példa:
A ciklus fejben lévő index < a.Length jellemző értékének vizsgálata valójában a háttérben egy metódushívás, ami az "a" tömb hosszát kérdezi le. Ez az információ azonban már ismert a ciklus megkezdése előtt is, így tehát a JIT fordító képes előre megvizsgálni, majd egy munkaváltozóba félretenni ezt az értéket még a végrehajtás előtt azért, hogy a "a.Lenght" hívásra később (a ciklus végrehajtása során) már ne kerüljön sor. Tanúlság: ennek a hatására a ciklus végrehajtás bizony sokkal gyorsabb lesz. A másik tanúlság az, hogy a progrmozó ne akarjon "okos" kódot írni, azaz ne akarjon okosabb lenni a JIT fordítónál és látván a helyzetet, előre kiemelni egy változóba az "a.Length" értékét, majd azt beletenni a ciklus fej részébe. Ez, eredményét tekintve persze ugyanolyan jó megoldás, mint amit a JIT fordító választ, csak az a baj, hogy teljesen felesleges, és rontja a kód olvashatóságát (mivel a JIT fordító is megtenné ugyanezt helyettünk). Nagyon fontos, hogy egy programozó tudja, miképp viselkedik az a környezet, ahol tevékenykedik, mert így legalább később egy csomó, csúnyácska megoldást elkerülhet.
Ugyanennél a példánál maradva képes a JIT fordító előre leellenőrizni, hogy lesz-e tömbhatár túllépés vagy sem. Érdekes mi? Egy még meg nem történt dolgot előre látni? Ahh, "akkor ezt itt most hogy?" (az idézet nem tőlem van). Hát úgy, hogy bizony a JIT mindent előre tud. Először is a tömb kezdő indexe 0, vagyis ez ellenőrizhető. A legmagasabb index vizsgálatra pedig a következő ((a.Length - 1) <= a.GetUpperBound(0)) kifejezés alkalmas. Ez is előre elvégezhető. Márpedig ha ez mind igaz (mármint hogy ha a JIT fordító előre tudja, hogy bizony nem lesz tömbhatár túllépés), akkor a teljes ciklus végrehajtási kódjából kihagyhatja az erre vonatkozó ellenőrzési utasításokat. Ettől kezdve ami kis ciklusunk hiperűrsebességre kapcsolhat és olyan gyorsan befejeződik, hogy időnk se lesz rá gondolni többet.
A fentebb leírtak is alátámasztják azt a jóslatot, miszerint: a menedzselt, JIT fordítóval generált kódé lesz a jövő. Milyen igaz.
Sajnos a nem 0 bázisú és többdimenziós tömböknél a JIT fordító nem képes előre elvégezni a tömbhatár túllépési ellenőrzéseket (legalábbis a .NET 2.0-ig ez így volt), ami azt jelenti, hogy ezek a részek továbbra is a cikluson belül maradnak, vagyis az ellenőrzés a tömbelemre való tényleges hivatkozáskor fog megtörténni.
Még további gyorsítási tippek, trükkök is léteznek, de erről majd máskor. |
Quot capita, tot sententiae...
No namewrote:
Szia! Látom, nem nagyon voltak eddig megjegyzések. Blogod indítójában írod: "Hogy őszinte legyek elég durva és provokatív dolgokat fogok itt mondani, elsősorban a számítástechnikáról, szoftverfejlesztésről". Végig nem olvastam az egész blogot (végül is 4 év), de amennyit igen, abból inkább azt a következtetést tudtam levonni, hogy bár sokszor értetlenül állsz a - fogalmazunk így - Linux oldal sárdobálásával, M$-t támadó megnyilatkozásaival szemben, magad is hasonló módon cselekszel. Nem baj, még csak azt sem mondom, hogy neked is jobb lenne, ha belátnád, hogy bár a saját szemedben túlnyomórészt szakmai alapokon nyugszik a véleményed, nem kis részben érzelmi alapokon állsz. És mint ilyen, az "ellentábor" az érveket, ellenérveket, véleményeket tendenciózus módon csoportosítod. Ez van. Az írod, azért nem engedsz kommentelni, mert időd sem lenne olvasni-válaszolni, meg különben is, ez a Te blogod. Így van. De azért csak csináltál egy vendégkönyvet, ahova ilyeneket is be lehet írni.
Üdv T
Apr. 2
Public folders
|
|||||||||||||||||||||||||||||||||||||||||||||||||
|
|