obstacks


Obsah:


Co to je obstacks?

Casto jsem hazel blato na cecko a ukazoval omezenost jeho alokace pameti (statickou velikost poli atd..) Kazdemu je jasne ze to neni az tak problem jazyka ale pojeti knihoven a ze by se dal do cecka pridat nejaky mechanizmus na neomezena pole-pole co by se samy zvetsovaly. Kazdemu je jasny ze se mu do toho nechce, protoze je to moc prace a vlastne zbytecne.. Je to take relativne slozite-realokovat po kazdem bajtu nejde..realokovat po blocich je lepsi ale porad se tam presunujou velke bloky dat, delat nejake treba kilove bloky zase komplikuje pouziti... Presto je tu jedno standardni reseni. Vetsinou o nem lidi vubec nevi. Ja jsem ho objevil nahodne asi pred rokem pri nahodnem prolejzani infa. Od te doby jsem ho parkrat pouzil a celkem se hodi. Jmenuje se obstacks a je soucastni standardni knihovny.

Inicializace

  1. princlidudit obstacks.h
  2. Nadeklarovat nejakou promenou typu struct obstack. POZOR! Struct obstack neni ukazatel na blok pameti, ktery chcete v budoucnosti rozsirovat! Knihovna obstacks ma svuj vlastni memorymanagement mnohem rychlejsi nez klasicke malloc. Specializovany na to, ze budete casto alokovat a uvolnovat. Klasicke malloc, free, realloc je dost pomale. Tohle je rychlejsi. Proste se snazi volani malloc/free minimalizovat. Dela jakousi cache. Struktura obstack ukazuje na prvni chrunk. To je blok pameti-dynamicky alokovany, do ktereho se data ukladaji. Pokud se dojde nakonec, automaticky se naalokuje dalsi.
  3. Nadefinavat dve makra:
         #define obstack_chunk_alloc xmalloc
         #define obstack_chunk_free free
    
    Tyto makra pouzivaji obstacky na alokovani/uvolnovani..Jake funkce k tomu pouzijete je na vas.
  4. Inicializovat obstack. K tomu slouzi funkce:
       void obstack_init (struct obstack *OBSTACK_PTR)
    
    takze nejak takto:
         static struct obstack myobstack;
         ...
         obstack_init (&myobstack);
    

Alokace v obstacku

Kdyz jse obstack inicializovan muzeme ho pouzit. Je tu nekolik funkci:
void * obstack_alloc (struct obstack *OBSTACK_PTR, size_t SIZE)
Tahle funkce funguje stejne jako malloc-naalokuje neinicializovany blok pameti a vrati ho. priklad:
     struct obstack string_obstack;

     char *
     copystring (char *string)
     {
       char *s = (char *) obstack_alloc (&string_obstack,
                                         strlen (string) + 1);
       memcpy (s, string, strlen (string));
       return s;
     }
Dalsi sikovna funkce na alokaci je:
void * obstack_copy (struct obstack *OBSTACK_PTR, void *ADDRESS, size_t SIZE)
Naalokuje misto velikosti SIZE, zkopiruje tam data a vrati adresu.
void * obstack_copy0 (struct obstack *OBSTACK_PTR, void *ADDRESS, size_t SIZE)
To same ale prida null na konec-hodi se na kopirovani stringu. Priklad:
     char *
     obstack_savestring (char *addr, size_t size)
     {
       return obstack_copy0 (&myobstack, addr, size);
     }

Uvolnovani v obstacku

void obstack_free (struct obstack *OBSTACK_PTR, void *OBJECT)
Jak ji pouzivat je asi jasne..

Makra/funkce

!POZOR!

Protoze obstacky chteji byt maximalne rychle, pouzivaji makra. To ale prinasi nevyhodu. Nektere parametry makra se vyhodnocuji dvakrat. Takze kdyz date volani:
     obstack_alloc (get_obstack (), 4);
Nemusi delat to co cekate, protoze get_obstack() muze byt volano nekolikrat a muze to cely zhroutit. K tomu je ke kazdemu makru jeste funkce. Pokud se rozhodnete pouzit funkci, musite napsat:
     char *x;
     void *(*funcp) ();
     /* Use the macro.  */
     x = (char *) obstack_alloc (obptr, size);
     /* Call the function.  */
     x = (char *) (obstack_alloc) (obptr, size);
     /* Take the address of the function.  */
     funcp = obstack_alloc;
Tohle vsechno ale nemusi platit na GCC, protoze tam se pouzivaji inline. Presto vam doporucuju hlidat si vedlejsi efekty u parametru obstackovych funkci.

Zvetsovani objektu

A jsme u toho. Rust objektu v obstackach je zalozen na celkem logicke uvaze, ze clovek vetsinou potrebuje objekt nejak vytvorit-po urcitou dobu ho nacitat a zvetsovat. Potom s nim uz nehejbe. Proto jsou tu funkce na zvetsovani objektu-samy vytvori rostouci objekt pri prvnim zavolani a potom obstack_finish, ktera rika dost! dal se uz rust nebude-zarizne ho. Potom je uz jakykoliv rust nemozny-to je dost vazny omezeni ale kupodivu v praxi vetsinou moc nevadi- proste si v nejhorsim pripade udelate nekolik struktur obstack..

!POZOR!

Pokud jeden objekt roste, cely obstack je zablokovany. Dokud rust objektu nezastavite-nezavolate obstack_finish nemuzete alokovat jine objekty!!

!POZOR!

Pri rustu objektu je klidne mozne ze se bude stehovat po pameti funkce:
void obstack_blank (struct obstack *OBSTACK_PTR, size_t SIZE)
Prida neicializovany data..
void obstack_grow (struct obstack *OBSTACK_PTR, void *DATA, size_t SIZE)
Prida data o velikosti SIZE
void obstack_grow0 (struct obstack *OBSTACK_PTR, void *DATA, size_t SIZE)
Prida data o velikosti SIZE a zakonci 0-hodi se pro rust stringu
void obstack_1grow (struct obstack *OBSTACK_PTR, char C)
Prida jenom jeden string-funguje trochu jako stream..
void * obstack_finish (struct obstack *OBSTACK_PTR)
Zarizne objekt a vrati vam ukazatel na nej..a muzete ho normalne pouzivat..
size_t obstack_object_size (struct obstack *OBSTACK_PTR)
Tahle funkce vrati velikost,kam uz objekt narostl
Takze typicke pouziti obstacku je treba nacteni cele pipe do pameti. Pokud chcete cist po stringu dela se to asi:
  1. inicializace obstacku
  2. smycka:
    1. 1)nacti znak
    2. 2)pridej ho pomoci obstack_1grow
  3. konec smycky pokud jsme na konci pipe
  4. pomoci obstack_1grow muzeme pridat 0 na konec, kdyz delame ze stringama
  5. pomoci obstack_finish ziskame ukazatel na nacteny fajl... a muzeme cist dalsi

Super fast rust!

Pokud potrebujete JESTE veci rychlost muzete pouzit tyto funkce. Zrychleni spociva v tom, ze obstack_grow musi vzdycky testovat jestli je v obstacku misto a pokud ne pridavat. Ale kdyby jste vedeli ze v obstacku je 4096 byte volno, muzete s klidem nacist 4 kila bez jakehokoliv testu. Na zjisteni volneho mista je:
size_t obstack_room (struct obstack *OBSTACK_PTR)
A na rust bez testu je:
void obstack_1grow_fast (struct obstack *OBSTACK_PTR, char C)
nebo:
void obstack_blank_fast (struct obstack *OBSTACK_PTR, size_t SIZE)
A kdyz v obstacku uz neni misto jednou pouzijete normalni rostouci funkci a ziskate zase fura volnyho mista...treba:
     void
     add_string (struct obstack *obstack, char *ptr, size_t len)
     {
       while (len > 0)
         {
           if (obstack_room (obstack) > len)
             {
               /* We have enough room: add everything fast.  */
               while (len-- > 0)
                 obstack_1grow_fast (obstack, *ptr++);
             }
           else
             {
               /* Not enough room. Add one character slowly,
                  which may copy to a new chunk and make room.  */
               obstack_1grow (obstack, *ptr++);
               len--;
             }
         }
     }

Status

void * obstack_base (struct obstack *OBSTACK_PTR) Vrati zacatek rostouciho objektu... void * obstack_next_free (struct obstack *OBSTACK_PTR) Vrati adresu prvniho volneho bytu v obstacku size_t obstack_object_size (struct obstack *OBSTACK_PTR) Velikost aktualniho rostouciho objektu. Stejna jako: obstack_next_free (OBSTACK_PTR) - obstack_base (OBSTACK_PTR)

ZAVER

Obstack sice v zadnem pripade nedosahuje kvalit lispu ale zase je to dabelsky rychly a sikovny prostredek na fura veci. Rozhodne je dost problemy, na ktere se hodi a je dobre o nem vedet..


Tento soubor je soucasti rozsahle sbirky skolicek na http://www.ucw.cz/~hubicka/skolicky

Take si muzete prohlidnout jeji puvodni textovou podobu

Nebo mi mailnout na hubicka@ucw.cz

Copyright (C) Jan Hubicka 1996