Funkce pro ladicí účely
Pro ladění algoritmů pracujícími s GMemChunk
y můžete využít následujících dvou funkcí, které poskytují některé důležité informace.
void g_mem_chunk_print(GMemChunk *mem_chunk);
…pošle na výstup ladicí informace o GMemChunk
u mem_chunk
. Vypíše název chunku (přiřazený funkcí g_mem_chunk_new()
– viz minulý díl), počet použitých bajtů a počet alokovaných bloků paměti.
void g_mem_chunk_info(void);
…vypíše informace o VŠECH existujících chuncích. Konkrétně vypíše počet aktuálně alokovaných GMemChunk
ů a zavolá funkci g_mem_chunk_print()
na každý z nich. Knihovna GLib si vnitřně uchovává seznam všech chunků, takže může takovou operaci provést, aniž byste museli explicitně všechny chunky „vyjmenovávat“ v argumentech. Je jen trochu mrzuté, že se ve výpisu objevují i chunky používané knihovnou GLib vnitřně – důsledným pojmenováváním chunků si však přehlednost jistě zachováte.
Příklad:
Dejme tomu, že jste vytvořili nový chunk chunk
pro ukládání datového typu gdouble
například takto:
chunk = g_mem_chunk_create(gdouble, 50, G_ALLOC_ONLY);
…a poté, co se na něm provedou nějaké operace, zavoláte funkci g_mem_chunk_print(chunk)
. Na obrazovku by se mělo vypsat informační hlášení:
GLib-INFO: gdouble mem chunks (50): 8000 bytes using 17 mem areas
…kde „ gdouble mem chunks (50)
“ je jméno chunku (takové mu přiřadila funkce g_mem_chunk_create()
). Hlášení nám dále říká, že chunk
zabírá v paměti 8000 bajtů a skládá se z celkem 17 bloků.
Volání funkce g_mem_chunk_info(chunk)
vypíše podobný řádek pro všechny chunky v programu (bohužel i ty, které si knihovna GLib vytváří pro své vnitřní účely):
GLib-INFO: 2 mem chunks GLib-INFO: GLib GTreeNode mem chunk: 340 bytes using 1 mem areas GLib-INFO: gdouble mem chunks (50): 8000 bytes using 17 mem areas
Toto hlášení nás informuje, že jsou vytvořeny dva chunky. Jeden systémový se jménem „ GLib GTreeNode mem chunk
“ zabírající jedním blokem 340 bajtů a druhý náš, který se jmenuje „ gdouble mem chunks (50)
“ a zabírá se svými sedmnácti bloky 8000 bajtů.
Ostatní funkce
void g_mem_chunk_reset(GMemChunk *mem_chunk);
Funkce g_mem_chunk_reset()
uvede GMemChunk
mem_chunk
do počátečního stavu. Všechny alokované bloky, až na první, se uvolní. Funkci lze použít na GMemChunk
y obou typů G_ALLOC_ONLY
a G_ALLOC_AND_FREE
.
void g_mem_chunk_clean(GMemChunk *mem_chunk);
…uvolní všechny bloky GMemChunk
u mem_chunk
, které jsou nevyužité (prázdné).
void g_blow_chunks(void);
…je další funkcí pracující se všemi chunky v programu. g_blow_chunks
zavolá funkci g_mem_chunk_clean()
na VŠECHNY GMemChunk
y v paměti.
Výkonnost GMemChunk
u
Pro zjištění účinnosti použití chunků jsem sestavil malý program, který měřil dobu vykonání operací alokování jednoho miliónu proměnných typu gint
– jednou standardním způsobem použitím funkce g_malloc
a podruhé s uložením v chunku. Srovnejte výsledky:
Velikost bloku | Čas pro alokaci v chunku |
1 * sizeof(gint) | 0,539 s |
10 * sizeof(gint) | 0,245 s |
100 * sizeof(gint) | 0,175 s |
1 000 * sizeof(gint) | 0,168 s |
10 000 * sizeof(gint) | 0,143 s |
Čas potřebný pro alokaci standardním způsobem: 0,482 s
V naměřených hodnotách jsou zahrnuty časy potřebné pouze pro alokaci – uvolňování z paměti se neměřilo. Upozorňuji, že uvedené hodnoty jsou pouze orientační – v žádném případě nezaručuji jejich dokonalou přesnost. Měření proběhlo v Linuxu s jádrem 2.2.12–20 (RedHat 6.1) na počítači s procesorem Intel Celeron 366 MHz, 96 MB RAM.
Z uvedených časů vyplývá, že nejoptimálnější velikost bloků by v tomto případě byl asi 10 – 100 násobek velikosti atomu ( sizeof(gint)
).
Pro úplnost ještě uvádím výpis testovacího prográmku:
/* Program testujici rychlost alokace pameti pouzitim GMemChunk. */
#include <glib.h>
#define COUNT 1000000
gint main(void) { GMemChunk *memchunk; gint x; gint *i, *p; GTimer *timer; /* nastroj pro mereni casu */ /*** test mem chunku ***/ timer = g_timer_new(); /* inicializace casomerice */ g_timer_start(timer); /* spusti casomeric */ memchunk = g_mem_chunk_new(NULL, sizeof(gint), 10000 * sizeof(gint), G_ALLOC_ONLY); /* vytvoreni noveho chunku */
for (x=0; x < COUNT; x++) {
i = g_mem_chunk_alloc(memchunk);
}
g_timer_stop(timer); /* zastavi casomeric */ g_mem_chunk_destroy(memchunk); /* odstrani chunk */ printf("Mem Chunk: %lf s\n", g_timer_elapsed(timer, NULL)); /*** test standardniho zpusobu (a la g_malloc()) ***/ g_timer_reset(timer); g_timer_start(timer); /* spusti casomeric */
for (x=0; x < COUNT; x++) {
/* pro solidnost vysledku dealokaci neprovadim - ctenar promine */
p = g_malloc(sizeof(gint));
}
g_timer_stop(timer); /* zastavi casomeric */ printf("Standard : %lf s\n", g_timer_elapsed(timer, NULL)); g_timer_destroy(timer); /* odstrani casomeric */ return(0); }
Tímto výpisem se pro dnešek rozloučíme. Příště si probereme už předminule avizované String Chunks.