ARCHIV |
|||||
Software (10844)
Distribuce (131)
Skripty (697)
Menu
Diskuze
Informace
|
Perl (143) - Struktura datových typů, správa pamětiJednou z nevýhod dynamicky typovaných jazyků je neefektivní spotřeba paměti. Měli bychom aspoň trochu tušit, jak si vede konkrétně Perl. Již jsme několikkrát narazili na měření rychlosti programů nebo jejich úseků. Zatím však vůbec nepřišla řeč na druhý významný systémový zdroj - paměť. Částečně i proto, že na její měření nám nestačí pouhé stopky. Programátora v Perlu nezajímá, co se kdy do paměti má ukládat. Má na to své "lidi". Alokaci a s tím související věci vůbec neřeší. Přesto je dobré, minimálně kvůli informaci, jak Perl funguje (nebo i kvůli jiným věcem, například debugging XS programů), alespoň tušit, jak Perl s pamětí přidělenou operačním systémem pracuje. Jako vždy jde mezi pamětí a rychlostí o kompromis. Pro dobrý příklad se vraťme k memoizaci. Pomocí ní můžeme zvýšit rychlost, ale vždy za cenu větších paměťových nároků. Jak na tom Perl tedy je? Vzhledem k velké rychlosti programů v Perlu se dá o paměťových nárocích leccos usuzovat a asi to nikoho moc nepřekvapí. Perl je významným spotřebitelem paměti, a tak si pro dnešek sundáme růžové brýle (ale jen na chvíli). Není tajemstvím, že Perl preferuje výkon nad úsporou paměti. Perl si raději i jednoduchou věc uloží, než aby ji musel znovu vypočítat (později se podíváme na konkrétní příklady). Když přidáme pokročilé datové typy a dynamické typování (v důsledku čehož má například integer minimálně 16 bajtů, float 24, řetězec 28 atd.), pramení z toho vysoká paměťová náročnost. Když libovolný proces potřebuje paměť, požádá o ni operační systém. Ten mu přidělí adresy, které může využívat. Perl se operačnímu systému nebojí říct si vždycky o velký kus (který celý momentálně ani nepotřebuje) a k zabírání paměti celkově přistupuje s filozofií "na horší časy". Když přestane část paměti využívat, tak ji operačnímu systému nevrací - co kdyby ji ještě na něco potřeboval. Vnitřní reprezentace proměnnýchProzkoumejme však práci s pamětí a s tím související vnitřní strukturu datových typů podrobněji pomocí několika z CPANu dostupných modulů. Budou nás zajímat hlavně tyto dva základní.
Proměnná obvykle mívá hodnotu. Ta může být jedním z následujících typů.
Tyto zkratky jsou vlastně datovými typy v jazyce C a každý typ má nějaké API, se kterým lze pracovat. Pro skalární hodnoty jsou všechny tyto funkce deklarovány v hlavičkovém souboru sv.h, který můžeme použít v našem C programu. Bývá umístěn v adresáři jména typu /usr/lib/perl5/5.12.3/i586-linux-thread-multi/CORE. Jsou zde nízkoúrovňové funkce pro vytvoření nového skaláru, přiřazení do skaláru (zvlášť pro celé číslo, desetinné číslo, řetězec atd.) a podobně. Například pro funkci, která z řetězce vytvoří novou skalární hodnotu, máme deklarovanou následující hlavičku.
Tomu se zde nebudeme více věnovat, ale koho by více zajímalo použití těchto hlavičkových souborů nebo vnitřní struktura jazyka obecně, nechť se podívá na manuálovou stránku perlguts(1). Skalární proměnná v pamětiSkalární proměnná může obsahovat buď celé číslo (IV), celé číslo bez znaménka (UV), desetinné číslo (NV), řetězec (PV) nebo speciální magickou hodnotu (MG). Později uvidíme, že může obsahovat i kombinace některých hodnot. Vytvořme na začátku nějakou proměnnou. Nic do ní nebudeme přiřazovat, pouze ji deklarujeme. Pomocí funkcí Dump a size můžeme zkoumat některé nízkoúrovňové informace.
Výstupem Dump je následující text.
Jak již víme, SV znamená Scalar Value. Za SV následuje NULL(0x0), což znamená, že proměnná je prázdná a na konci řádku máme její adresu. V REFCNT je počet odkazů na tuto proměnnou a ve FLAGS jsou různé flagy (například, že je proměnná deklarovaná pomocí my, také zde ale můžeme mít informaci, že jde o objekt atd.). Výstupem funkce size pak je počet bajtů, které proměnná zabírá. V našem případě je to 16! To je dost vysoké číslo na to, že tato proměnná nedrží žádnou informaci. Každá proměnná tedy zabírá minimálně 16 bajtů v paměti. Dále můžeme zkusit do proměnné přiřadit řetězec.
Opět se můžeme podívat na výstup Dump.
Přibylo zde několik nových řádků. CUR je počet znaků a LEN počet alokovaných míst pro znaky. Ještě tedy můžeme trochu přidat a pak bude potřeba přialokovat další paměť. LEN je vždy alespoň o 1 větší, protože je zde započítán i jeden speciální znak. S tím souvisí i velikost. Ta vzrostla na 32 bajtů. Zajímavé je zkusit si přidávat nebo ubírat postupně znaky. Uvidíme, že velikost vždycky jednou za čas podle LEN a CUR skočí, ale často se nemění. Také je zajímavé vyzkoušet přidávat jiné než ASCII znaky. Dále zde je PV (pointer value), což, znamená, jak opět víme, že jde o řetězec. Zajímavé je, že pokud řetězec někde použijeme jako číslo, změní se PV na PVIV a bude mít jak řádek PV tak i IV - tedy pro oba datové typy bude celá informace uložena zvlášť. Toto lze udělat i pro další dvojice, například můžeme dostat PVUV nebo PVNV. Podívejme se tak potom vypadá výpis Dump pro následující situaci.
Zde je výsledek.
Takové chování se samozřejmě se projeví i na paměťové náročnosti - uložené je obojí, takže celkem je zabráno 32 bajtů. Tady je pěkně vidět, jak Perl s pamětí hospodaří. Ještě jsme na začátku zmínili magický typ. Taková proměnná se vyznačuje tím, že má nějakou speciální vlastnost, například spojení s nějakou akcí. To, že k takové proměnné přistoupíme může například způsobit spuštění nějakého jiného procesu. Typicky jde o navázané proměnné. Několik speciálních proměnných má magický typ, například $!. Funkce Dump zde odhalí následující.
Další způsob, jak získat magickou hodnotu, je režim nakažení. Každá nakažená proměnná je typu MG. S využitím této vlastnosti pak funguje například modul Safe, který umožňuje s nakaženými daty například i provedení eval, pokud není výsledkem vyhodnocení nějaká zakázaná instrukce. Odkaz v pamětiDump zobrazuje u odkazů hierarchii. Takto mohou vypadat informace pro odkaz na číslo 123.
Funkce size ukazuje v případě odkazu velikost toho, na co se odkazuje, což taky většinou chceme. V případě odkazu na odkaz na hodnotu však již dostaneme skutečně velikost odkazu na hodnotu, nikoliv hodnotu. Volání Dump pro odkazy se bude hodit zejména pro zkoumání polí a hashů. Pole v pamětiPodívejme se i na ostatní datové typy. Pole (AV) je ve skutečnosti dynamickým seznamem odkazů na skaláry. V perlguts(1) nebo hlavičkovém souboru av.h opět nalezneme API seznamů. Tiskneme-li výstup Dump u odkazu na pole (například pole [1 .. 8]), získáme jak celkovou analýzu (počet prvků, poslední prvek, typ hodnot, kolik paměti je alokováno a kdy bude potřeba přialokovat atd.) tak i postupně informace o několika úvodních prvcích. Výstup tedy vypadá takto.
Funkce size nám u polí přestává fungovat tak, jak bychom si představovali. Měří pouze velikost struktury, nikoliv už dat v ní uložených. Pro každé stejně velké pole vrátí size stejnou hodnotu. Abychom získali velikost i s daty, je třeba použít total_size, která následuje reference. Například struktura následujícího desetiprvkového pole zabere 28 bajtů. S obsahem je to však dohromady už 1028.
Hash v pamětiPodobné informace získáme i pro hashe. Hash je dvojice jednoznačného řetězce a ukazatele na skalár. Řetězec lze přitom namapovat na celé číslo a pak lze hash reprezentovat jako obyčejné dynamické pole. Mapování přitom funguje poměrně zajímavě. Na bázi nějakého hashovacího algoritmu (poznamenejme, že odtud pochází název pro datový typ hash) totiž ke každému klíči přiřadíme celé číslo mezi 1 a 2n, kde n je nějak zvolené. Přitom může docházet ke kolizím! To znamená, že se dva klíče mohou namapovat na stejné číslo. Skutečnost je taková, že se vlastně uchovává pro každé číslo spojový seznam všech hodnot, jejichž klíče se na toto číslo zobrazily hashovací funkcí. Ideální stav by byl, kdyby byly prvky hashe v poli co nejrovnoměrněji rozloženy (to jest, aby spojové seznamy měly pokud možno právě jeden prvek). To zajišťujeme za prvé vhodným hashovacím algoritmem. S ním mimochodem můžeme pomocí API manipulovat (třeba i proto, že máme nějak specificky zvolenou množinu klíčů hashe a výchozí algoritmus tím pádem nevyhovuje, protože tyto klíče mapuje často na stejné hodnoty). Stačí předat číslo prvku (tj. námi alternativně zahashovanou hodnotu) funkci hv_store deklarované v hlavičkovém souboru hv.h, která ukládá prvek hashe do tabulky. Výchozí hashovací algoritmus je definován jako makro PERL_HASH(hash, key, klen), kde klen je délka klíče.
Zopakujme, že pokud tedy proženeme náš klíč tímto algoritmem, získáme v proměnné hash nějaké číslo, pod které se uloží jako další prvek spojového seznamu naše hodnota. A za druhé co nejrovnoměrnější rozdělení zajišťujeme vhodnou volbou n. Jak hash roste na velikosti, roste i n. Pokud počet prvků hashe dosáhne 2n (tj. na jedno číslo jeden prvek hashe), rozšíří se tabulka automaticky zvýšením n o 1. Dodejme, že tiskneme-li hash ve skalárním kontextu, získáme dvě čísla oddělená lomítkem:
Například následující kód vytiskne 2/8.
Kdybychom přidali dalších 6 prvků, dostali bychom na výstupu 8/8. Kdybychom však dostali ještě jeden další prvek, již by se vytisklo 9/16, protože by se zvýšilo n. Jakmile se zvýší, není třeba nic přepočítávat. Prvky již přítomné si svoje číslo ponechají nadále. Čím vyšší je poměr těchto dvou čísel, tím je algoritmus paměťově úspornější. Naopak, čím je poměr nižší, tím rychleji jsme schopni z hashe číst, protože je menší pravděpodobnost, že budeme nuceni prohledávat delší spojový seznam. Uveďme nevýznamnou, avšak zajímavou poznámku na okraj. Pokud očekáváme hodně prvků, lze velikost tabulky nastavit přímo v programu následujícím příkazem.
Díky němu bude překladač vědět, že má dynamické pole naalokovat větší než původně chtěl, protože to časem bude potřeba. Po jeho zavolání se n nastaví tak, aby 2n byla nejbližší vyšší mocnina dvou. Přidáním tohoto řádku by tak měl předchozí kód za následek vytisknutí 2/1024. Podívejme se ale, jak bude vypadat výstup funkce Dump u hashů. Nyní k němu asi už není třeba podrobného komentáře.
Ostatní datové typySamozřejmě to nejsou všechny datové typy. Typegloby jsou reprezentovány hashovou tabulkou a funkce Dump ukáže například následující.
Pro úplnost se podívejme na výstup Dump pro anonymní podprogram.
Pro více informací lze opět doporučit stránku perlguts(1). MemchmarkExistuje analogie modulu Benchmark pro měření paměťových nároků. Memchmark exportuje funkci cmpthese, která spočítá, kolik paměti zabírá který podprogram. Funguje tak, že vytvoří nový proces, zjišťuje v intervalech jeho paměťové nároky a hledá maximální hodnotu. Díky tomu se však nemusí modul vždy chovat 100% správně. Zde je malý příklad použití.
Na výstupu dostaneme přibižnou paměťovou náročnost jednotlivých podprogramů.
Související články
Předchozí Celou kategorii (seriál) Další
Perl (1) - Dávka teorie na úvod
Perl (2) - Úvod do syntaxe Perl (3) - Proměnné Perl (4) - Čísla a řetězce Perl (5) - Podmínky Perl (6) - Pravdivostní výrazy Perl (7) - Vstup poprvé Perl (8) - Některé základní vestavěné funkce Perl (9) - Cykly Perl (10) - Další řídící struktury Perl (11) - Pole - úvod Perl (12) - Pole - základní operace Perl (13) - Hashe Perl (14) - Další nástroje pro seznamy Perl (15) - Výchozí proměnná, heredoc, symbolické odkazy Perl (16) - Regulární výrazy - začínáme Perl (17) - Regulární výrazy - kotvy Perl (18) - Regulární výrazy - množiny znaků Perl (19) - Regulární výrazy - opakování a kvantifikátory Perl (20) - Regulární výrazy - magické závorky Perl (21) - Regulární výrazy - nahrazování Perl (22) - Regulární výrazy - přepínače Perl (23) - Regulární výrazy - rozšířené vzory Perl (24) - Regulární výrazy - příklady Perl (25) - Regulární výrazy - závěr Perl (26) - Podprogramy Perl (27) - Prototypy Perl (28) - Rozsahy platnosti proměnných Perl (29) - Úvod k práci se soubory Perl (30) - Práce se soubory Perl (31) - Testování souborů Perl (32) - Jiné typy souborů Perl (33) - Formátování výstupu - printf Perl (34) - Formátování výstupu - formáty Perl (35) - Vestavěný debugger Perl (36) - Grafické debuggery Perl (37) - Začínáme s moduly Perl (38) - Rozhraní modulu Perl (39) - Pragma Perl (40) - Dodatky k modulům Perl (41) - CPAN Perl (42) - Argumenty příkazového řádku Perl (43) - Přepínače Perl (44) - Dlouhé přepínače Perl (45) - Odkazy Perl (46) - Užití odkazů a anonymní data Perl (47) - Složitější datové struktury Perl (48) - Libovolně složité datové struktury Perl (49) - Tabulky symbolů a typegloby Perl (50) - Uzávěry a iterátory Perl (51) - Signály Perl (52) - Externí příkazy Perl (53) - Režim nakažení Perl (54) - Fork Perl (55) - Eval Perl (56) - Volby příkazu perl Perl (57) - Jednořádkové skripty Perl (58) - OOP - úvod Perl (59) - OOP - typické použití Perl (60) - OOP - dědičnost Perl (61) - OOP - přínos a užití dědičnosti Perl (62) - OOP - přetěžování Perl (63) - OOP - závěr Perl (64) - Projekt - čtečka sportovních výsledků Perl (65) - Projekt - získání dat Perl (66) - Projekt - výběr zápasů a podrobnosti Perl (67) - Projekt - dokončujeme modul Perl (68) - Projekt - zobrazení zápasů Perl (69) - Projekt - online přenos Perl (70) - Plain Old Documentation Perl (71) - Navazování proměnných Perl (72) - Navazování složitějších datových typů Perl (73) - DBM Perl (74) - Sockety Perl (75) - Obsluha více klientů Perl (76) - Síťová hra v kostky Perl (77) - Služby internetu Perl (78) - Databáze - úvod Perl (79) - Databáze - manipulace s daty Perl (80) - Databáze - závěrečné poznámky Perl (81) - CGI - příprava webového serveru Perl (82) - CGI - první skripty Perl (83) - CGI - získávání dat od uživatele Perl (84) - CGI - usnadnění tvorby skriptů pomocí modulu CGI Perl (85) - CGI - generování dokumentu modulem CGI Perl (86) - CGI - cookies Perl (87) - CGI - příklad aplikace Perl (88) - CGI - závěr Perl (89) - Mason - snadné psaní webů Perl (90) - Mason - speciální bloky Perl (91) - Mason - handlery Perl (92) - Mason - závěr Perl (93) - Catalyst - MVC framework pro Perl Perl (94) - Catalyst - základy pro psaní aplikace Perl (95) - Catalyst - šablony Perl (96) - Catalyst - spolupráce s databází Perl (97) - Curses - tvorba textových uživatelských rozhraní Perl (98) - Curses - pozicování a okna Perl (99) - Curses - měření rychlosti psaní Perl (100) - Curses - použití hotových widgetů Perl (101) - Curses - jednoduchý textový editor Perl (102) - Rozšiřování Perlu pomocí XS Perl (103) - Rozšiřování Perlu pomocí SWIG Perl (104) - Testování rychlosti Perl (105) - Testování programových jednotek Perl (106) - Debugování pomocí komentářů Perl (107) - Moose - moderní objektový systém Perl (108) - Moose - základní vlastnosti Perl (109) - Moose - role Perl (110) - Moose - meta API Perl (111) - Pokročilá práce se seznamy Perl (112) - Práce s PDF Perl (113) - Práce s archivy Perl (114) - Tk - úvod Perl (115) - Tk - umísťování widgetů Perl (116) - Tk - základní widgety Perl (117) - Tk - některé pokročilejší widgety Perl (118) - Tk - čas a události Perl (119) - Tk - CD man Perl (120) - Wx - základní práce s widgety Perl (121) - Wx - události Perl (122) - Gtk2 - úvod Perl (123) - Gtk2 - základní práce s obrázky Perl (124) - Gtk2 - události a čas Perl (125) - Gtk2 - vlastní widgety Perl (126) - Gtk2 - textové okno a práce s pozicemi Perl (127) - Gtk2 - hierarchické seznamy Perl (128) - Gtk2 - dialogy Perl (129) - Gtk2 - skládání widgetů Perl (130) - Gtk2 - menu a toolbary Perl (131) - Gtk2 - transparentní okna, tray ikona, výběr souborů Perl (132) - Gtk2 - drag&drop, druid Perl (133) - Gtk2 - úpravy vzhledu aplikací pomocí rc Perl (134) - Gtk2 - Glade Interface Designer Perl (135) - XML - čtení a zápis Perl (136) - XML - DOM a SAX přístupy Perl (137) - Vlákna Perl (138) - Memoizace - cachování podprogramů Perl (139) - Profilling - efektivní odhalování pomalých míst v programu Perl (140) - Profilling - píšeme si vlastní profiler / debugger Perl (141) - Formátování kódu, deparsování, perltidy Perl (142) - Způsoby konfigurování Perl (144) - POE - událostmi řízené programování Perl (145) - POE - aplikace typu klient-server Perl (146) - Perl 6 - jazyk budoucnosti Perl (147) - Perl 6 - regulární výrazy, nové operátory Perl (148) - Perl Culture Perl (149) - Závěr Pozvánka na Český Perl Workshop Perl 5.22.0 a vše okolo Perl 5.24.0 a vše okolo Předchozí Celou kategorii (seriál) Další
|
Vyhledávání software
Vyhledávání článků
28.11.2018 23:56 /František Kučera 12.11.2018 21:28 /Redakce Linuxsoft.cz 6.11.2018 2:04 /František Kučera 4.10.2018 21:30 /Ondřej Čečák 18.9.2018 23:30 /František Kučera 9.9.2018 14:15 /Redakce Linuxsoft.cz 12.8.2018 16:58 /František Kučera 16.7.2018 1:05 /František Kučera
Poslední diskuze
31.7.2023 14:13 /
Linda Graham 30.11.2022 9:32 /
Kyle McDermott 13.12.2018 10:57 /
Jan Mareš 2.12.2018 23:56 /
František Kučera 5.10.2018 17:12 /
Jakub Kuljovsky | |||
ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2024) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze |