obstacks
Obsah:
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.
- princlidudit obstacks.h
- 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.
- 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.
- Inicializovat obstack. K tomu slouzi funkce:
void obstack_init (struct obstack *OBSTACK_PTR)
takze nejak takto:
static struct obstack myobstack;
...
obstack_init (&myobstack);
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);
}
void obstack_free (struct obstack *OBSTACK_PTR, void *OBJECT)
Jak ji pouzivat je asi jasne..
!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.
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:
- inicializace obstacku
- smycka:
- 1)nacti znak
- 2)pridej ho pomoci obstack_1grow
- konec smycky pokud jsme na konci pipe
- pomoci obstack_1grow muzeme pridat 0 na konec, kdyz delame ze stringama
- 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