Hlavní navigace

GLib: Bleskové operace s pamětí (2)

Michal Burda

Minule jsme nakousli zajímavé téma o možnosti urychlení alokace paměti pomocí GMemChunků. Dnes se podíváme na další funkce pro práci s nimi.

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_ONLYG_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:

Tabulka č. 55
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.

Našli jste v článku chybu?