2 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4 * Authors: Michael Zucchi <notzed@ximian.com>
5 * Jacob Berkman <jacob@ximian.com>
7 * This library is free software you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation.
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
23 #include <string.h> /* memset() */
31 struct timeval timeit_start;
33 static time_start (const gchar *desc)
35 gettimeofday (&timeit_start, NULL);
36 printf ("starting: %s\n", desc);
39 static time_end (const gchar *desc)
44 gettimeofday (&end, NULL);
45 diff = end.tv_sec * 1000 + end.tv_usec / 1000;
46 diff -= timeit_start.tv_sec * 1000 + timeit_start.tv_usec / 1000;
48 "%s took %ld.%03ld seconds\n",
49 desc, diff / 1000, diff % 1000);
56 typedef struct _MemChunkFreeNode {
57 struct _MemChunkFreeNode *next;
62 guint blocksize; /* number of atoms in a block */
63 guint atomsize; /* size of each atom */
64 GPtrArray *blocks; /* blocks of raw memory */
65 struct _MemChunkFreeNode *free;
70 * @atomcount: the number of atoms stored in a single malloc'd block of memory
71 * @atomsize: the size of each allocation
73 * Create a new #EMemChunk header. Memchunks are an efficient way to
74 * allocate and deallocate identical sized blocks of memory quickly, and
77 * e_memchunks are effectively the same as gmemchunks, only faster (much),
78 * and they use less memory overhead for housekeeping.
80 * Returns: a new #EMemChunk
83 e_memchunk_new (gint atomcount,
86 EMemChunk *memchunk = g_malloc (sizeof (*memchunk));
88 memchunk->blocksize = atomcount;
89 memchunk->atomsize = MAX (atomsize, sizeof (MemChunkFreeNode));
90 memchunk->blocks = g_ptr_array_new ();
91 memchunk->free = NULL;
98 * @memchunk: an #EMemChunk
100 * Allocate a new atom size block of memory from an #EMemChunk.
101 * Free the returned atom with e_memchunk_free().
103 * Returns: (transfer full): an allocated block of memory
106 e_memchunk_alloc (EMemChunk *memchunk)
116 mem = ((gchar *) f) + (f->atoms * memchunk->atomsize);
119 memchunk->free = memchunk->free->next;
123 b = g_malloc (memchunk->blocksize * memchunk->atomsize);
124 g_ptr_array_add (memchunk->blocks, b);
125 f = (MemChunkFreeNode *) &b[memchunk->atomsize];
126 f->atoms = memchunk->blocksize - 1;
135 * @memchunk: an #EMemChunk
137 * Allocate a new atom size block of memory from an #EMemChunk,
138 * and fill the memory with zeros. Free the returned atom with
141 * Returns: (transfer full): an allocated block of memory
144 e_memchunk_alloc0 (EMemChunk *memchunk)
148 mem = e_memchunk_alloc (memchunk);
149 memset (mem, 0, memchunk->atomsize);
156 * @memchunk: an #EMemChunk
157 * @mem: address of atom to free
159 * Free a single atom back to the free pool of atoms in the given
163 e_memchunk_free (EMemChunk *memchunk,
168 /* Put the location back in the free list. If we knew if the
169 * preceeding or following cells were free, we could merge the
170 * free nodes, but it doesn't really add much. */
172 f->next = memchunk->free;
176 /* We could store the free list sorted - we could then do the above,
177 * and also probably improve the locality of reference properties for
178 * the allocator. (And it would simplify some other algorithms at
179 * that, but slow this one down significantly.) */
184 * @memchunk: an #EMemChunk
186 * Clean out the memchunk buffers. Marks all allocated memory as free blocks,
187 * but does not give it back to the system. Can be used if the memchunk
188 * is to be used repeatedly.
191 e_memchunk_empty (EMemChunk *memchunk)
193 MemChunkFreeNode *f, *h = NULL;
196 for (i = 0; i < memchunk->blocks->len; i++) {
197 f = (MemChunkFreeNode *) memchunk->blocks->pdata[i];
198 f->atoms = memchunk->blocksize;
207 struct _cleaninfo *next;
210 gint size; /* just so tree_search has it, sigh */
214 tree_compare (struct _cleaninfo *a,
215 struct _cleaninfo *b)
217 if (a->base < b->base)
219 else if (a->base > b->base)
225 tree_search (struct _cleaninfo *a,
228 if (a->base <= mem) {
229 if (mem < &a->base[a->size])
238 * @memchunk: an #EMemChunk
240 * Scan all empty blocks and check for blocks which can be free'd
241 * back to the system.
243 * This routine may take a while to run if there are many allocated
244 * memory blocks (if the total number of allocations is many times
245 * greater than atomcount).
248 e_memchunk_clean (EMemChunk *memchunk)
253 struct _cleaninfo *ci, *hi = NULL;
256 if (memchunk->blocks->len == 0 || f == NULL)
259 /* first, setup the tree/list so we can map
260 * free block addresses to block addresses */
261 tree = g_tree_new ((GCompareFunc) tree_compare);
262 for (i = 0; i < memchunk->blocks->len; i++) {
263 ci = alloca (sizeof (*ci));
265 ci->base = memchunk->blocks->pdata[i];
266 ci->size = memchunk->blocksize * memchunk->atomsize;
267 g_tree_insert (tree, ci, ci);
272 /* now, scan all free nodes, and count them in their tree node */
274 ci = g_tree_search (tree, (GCompareFunc) tree_search, f);
276 ci->count += f->atoms;
278 g_warning ("error, can't find free node in memory block\n");
283 /* if any nodes are all free, free & unlink them */
286 if (ci->count == memchunk->blocksize) {
287 MemChunkFreeNode *prev = NULL;
291 if (tree_search (ci, (gpointer) f) == 0) {
292 /* prune this node from our free-node list */
294 prev->next = f->next;
296 memchunk->free = f->next;
304 g_ptr_array_remove_fast (memchunk->blocks, ci->base);
310 g_tree_destroy (tree);
314 * e_memchunk_destroy:
315 * @memchunk: an #EMemChunk
317 * Free the memchunk header, and all associated memory.
320 e_memchunk_destroy (EMemChunk *memchunk)
324 if (memchunk == NULL)
327 for (i = 0; i < memchunk->blocks->len; i++)
328 g_free (memchunk->blocks->pdata[i]);
330 g_ptr_array_free (memchunk->blocks, TRUE);
337 #define CHUNK_SIZE (20)
338 #define CHUNK_COUNT (32)
351 s = strv_set (s, 1, "Testing 1");
352 s = strv_set (s, 2, "Testing 2");
353 s = strv_set (s, 3, "Testing 3");
354 s = strv_set (s, 4, "Testing 4");
355 s = strv_set (s, 5, "Testing 5");
356 s = strv_set (s, 6, "Testing 7");
358 for (i = 0; i < 8; i++) {
359 printf ("s[%d] = %s\n", i, strv_get (s, i));
364 printf ("packing ...\n");
367 for (i = 0; i < 8; i++) {
368 printf ("s[%d] = %s\n", i, strv_get (s, i));
371 printf ("setting ...\n");
373 s = strv_set_ref (s, 1, "Testing 1 x");
375 for (i = 0; i < 8; i++) {
376 printf ("s[%d] = %s\n", i, strv_get (s, i));
379 printf ("packing ...\n");
382 for (i = 0; i < 8; i++) {
383 printf ("s[%d] = %s\n", i, strv_get (s, i));
389 time_start ("Using memchunks");
390 mc = memchunk_new (CHUNK_COUNT, CHUNK_SIZE);
391 for (i = 0; i < 1000000; i++) {
392 mem = memchunk_alloc (mc);
394 memchunk_free (mc, mem);
397 memchunk_destroy (mc);
398 time_end ("allocating 1000000 memchunks, freeing 500k");
400 time_start ("Using gmemchunks");
401 gmc = g_mem_chunk_new (
402 "memchunk", CHUNK_SIZE,
403 CHUNK_SIZE * CHUNK_COUNT,
405 for (i = 0; i < 1000000; i++) {
406 mem = g_mem_chunk_alloc (gmc);
408 g_mem_chunk_free (gmc, mem);
411 g_mem_chunk_destroy (gmc);
412 time_end ("allocating 1000000 gmemchunks, freeing 500k");
414 time_start ("Using memchunks");
415 mc = memchunk_new (CHUNK_COUNT, CHUNK_SIZE);
416 for (i = 0; i < 1000000; i++) {
417 mem = memchunk_alloc (mc);
420 memchunk_destroy (mc);
421 time_end ("allocating 1000000 memchunks");
423 time_start ("Using gmemchunks");
424 gmc = g_mem_chunk_new (
425 "memchunk", CHUNK_SIZE,
426 CHUNK_COUNT * CHUNK_SIZE,
428 for (i = 0; i < 1000000; i++) {
429 mem = g_mem_chunk_alloc (gmc);
432 g_mem_chunk_destroy (gmc);
433 time_end ("allocating 1000000 gmemchunks");
435 time_start ("Using malloc");
436 for (i = 0; i < 1000000; i++) {
439 time_end ("allocating 1000000 malloc");