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 program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
25 #include <string.h> /* memset() */
33 struct timeval timeit_start;
35 static time_start (const gchar *desc)
37 gettimeofday (&timeit_start, NULL);
38 printf ("starting: %s\n", desc);
41 static time_end (const gchar *desc)
46 gettimeofday (&end, NULL);
47 diff = end.tv_sec * 1000 + end.tv_usec / 1000;
48 diff -= timeit_start.tv_sec * 1000 + timeit_start.tv_usec / 1000;
50 "%s took %ld.%03ld seconds\n",
51 desc, diff / 1000, diff % 1000);
58 typedef struct _MemChunkFreeNode {
59 struct _MemChunkFreeNode *next;
64 guint blocksize; /* number of atoms in a block */
65 guint atomsize; /* size of each atom */
66 GPtrArray *blocks; /* blocks of raw memory */
67 struct _MemChunkFreeNode *free;
72 * @atomcount: the number of atoms stored in a single malloc'd block of memory
73 * @atomsize: the size of each allocation
75 * Create a new #EMemChunk header. Memchunks are an efficient way to
76 * allocate and deallocate identical sized blocks of memory quickly, and
79 * e_memchunks are effectively the same as gmemchunks, only faster (much),
80 * and they use less memory overhead for housekeeping.
82 * Returns: a new #EMemChunk
85 e_memchunk_new (gint atomcount,
88 EMemChunk *memchunk = g_malloc (sizeof (*memchunk));
90 memchunk->blocksize = atomcount;
91 memchunk->atomsize = MAX (atomsize, sizeof (MemChunkFreeNode));
92 memchunk->blocks = g_ptr_array_new ();
93 memchunk->free = NULL;
100 * @memchunk: an #EMemChunk
102 * Allocate a new atom size block of memory from an #EMemChunk.
103 * Free the returned atom with e_memchunk_free().
105 * Returns: (transfer full): an allocated block of memory
108 e_memchunk_alloc (EMemChunk *memchunk)
118 mem = ((gchar *) f) + (f->atoms * memchunk->atomsize);
121 memchunk->free = memchunk->free->next;
125 b = g_malloc (memchunk->blocksize * memchunk->atomsize);
126 g_ptr_array_add (memchunk->blocks, b);
127 f = (MemChunkFreeNode *) &b[memchunk->atomsize];
128 f->atoms = memchunk->blocksize - 1;
137 * @memchunk: an #EMemChunk
139 * Allocate a new atom size block of memory from an #EMemChunk,
140 * and fill the memory with zeros. Free the returned atom with
143 * Returns: (transfer full): an allocated block of memory
146 e_memchunk_alloc0 (EMemChunk *memchunk)
150 mem = e_memchunk_alloc (memchunk);
151 memset (mem, 0, memchunk->atomsize);
158 * @memchunk: an #EMemChunk
159 * @mem: address of atom to free
161 * Free a single atom back to the free pool of atoms in the given
165 e_memchunk_free (EMemChunk *memchunk,
170 /* Put the location back in the free list. If we knew if the
171 * preceeding or following cells were free, we could merge the
172 * free nodes, but it doesn't really add much. */
174 f->next = memchunk->free;
178 /* We could store the free list sorted - we could then do the above,
179 * and also probably improve the locality of reference properties for
180 * the allocator. (And it would simplify some other algorithms at
181 * that, but slow this one down significantly.) */
186 * @memchunk: an #EMemChunk
188 * Clean out the memchunk buffers. Marks all allocated memory as free blocks,
189 * but does not give it back to the system. Can be used if the memchunk
190 * is to be used repeatedly.
193 e_memchunk_empty (EMemChunk *memchunk)
195 MemChunkFreeNode *f, *h = NULL;
198 for (i = 0; i < memchunk->blocks->len; i++) {
199 f = (MemChunkFreeNode *) memchunk->blocks->pdata[i];
200 f->atoms = memchunk->blocksize;
209 struct _cleaninfo *next;
212 gint size; /* just so tree_search has it, sigh */
216 tree_compare (struct _cleaninfo *a,
217 struct _cleaninfo *b)
219 if (a->base < b->base)
221 else if (a->base > b->base)
227 tree_search (struct _cleaninfo *a,
230 if (a->base <= mem) {
231 if (mem < &a->base[a->size])
240 * @memchunk: an #EMemChunk
242 * Scan all empty blocks and check for blocks which can be free'd
243 * back to the system.
245 * This routine may take a while to run if there are many allocated
246 * memory blocks (if the total number of allocations is many times
247 * greater than atomcount).
250 e_memchunk_clean (EMemChunk *memchunk)
255 struct _cleaninfo *ci, *hi = NULL;
258 if (memchunk->blocks->len == 0 || f == NULL)
261 /* first, setup the tree/list so we can map
262 * free block addresses to block addresses */
263 tree = g_tree_new ((GCompareFunc) tree_compare);
264 for (i = 0; i < memchunk->blocks->len; i++) {
265 ci = alloca (sizeof (*ci));
267 ci->base = memchunk->blocks->pdata[i];
268 ci->size = memchunk->blocksize * memchunk->atomsize;
269 g_tree_insert (tree, ci, ci);
274 /* now, scan all free nodes, and count them in their tree node */
276 ci = g_tree_search (tree, (GCompareFunc) tree_search, f);
278 ci->count += f->atoms;
280 g_warning ("error, can't find free node in memory block\n");
285 /* if any nodes are all free, free & unlink them */
288 if (ci->count == memchunk->blocksize) {
289 MemChunkFreeNode *prev = NULL;
293 if (tree_search (ci, (gpointer) f) == 0) {
294 /* prune this node from our free-node list */
296 prev->next = f->next;
298 memchunk->free = f->next;
306 g_ptr_array_remove_fast (memchunk->blocks, ci->base);
312 g_tree_destroy (tree);
316 * e_memchunk_destroy:
317 * @memchunk: an #EMemChunk
319 * Free the memchunk header, and all associated memory.
322 e_memchunk_destroy (EMemChunk *memchunk)
326 if (memchunk == NULL)
329 for (i = 0; i < memchunk->blocks->len; i++)
330 g_free (memchunk->blocks->pdata[i]);
332 g_ptr_array_free (memchunk->blocks, TRUE);
339 #define CHUNK_SIZE (20)
340 #define CHUNK_COUNT (32)
353 s = strv_set (s, 1, "Testing 1");
354 s = strv_set (s, 2, "Testing 2");
355 s = strv_set (s, 3, "Testing 3");
356 s = strv_set (s, 4, "Testing 4");
357 s = strv_set (s, 5, "Testing 5");
358 s = strv_set (s, 6, "Testing 7");
360 for (i = 0; i < 8; i++) {
361 printf ("s[%d] = %s\n", i, strv_get (s, i));
366 printf ("packing ...\n");
369 for (i = 0; i < 8; i++) {
370 printf ("s[%d] = %s\n", i, strv_get (s, i));
373 printf ("setting ...\n");
375 s = strv_set_ref (s, 1, "Testing 1 x");
377 for (i = 0; i < 8; i++) {
378 printf ("s[%d] = %s\n", i, strv_get (s, i));
381 printf ("packing ...\n");
384 for (i = 0; i < 8; i++) {
385 printf ("s[%d] = %s\n", i, strv_get (s, i));
391 time_start ("Using memchunks");
392 mc = memchunk_new (CHUNK_COUNT, CHUNK_SIZE);
393 for (i = 0; i < 1000000; i++) {
394 mem = memchunk_alloc (mc);
396 memchunk_free (mc, mem);
399 memchunk_destroy (mc);
400 time_end ("allocating 1000000 memchunks, freeing 500k");
402 time_start ("Using gmemchunks");
403 gmc = g_mem_chunk_new (
404 "memchunk", CHUNK_SIZE,
405 CHUNK_SIZE * CHUNK_COUNT,
407 for (i = 0; i < 1000000; i++) {
408 mem = g_mem_chunk_alloc (gmc);
410 g_mem_chunk_free (gmc, mem);
413 g_mem_chunk_destroy (gmc);
414 time_end ("allocating 1000000 gmemchunks, freeing 500k");
416 time_start ("Using memchunks");
417 mc = memchunk_new (CHUNK_COUNT, CHUNK_SIZE);
418 for (i = 0; i < 1000000; i++) {
419 mem = memchunk_alloc (mc);
422 memchunk_destroy (mc);
423 time_end ("allocating 1000000 memchunks");
425 time_start ("Using gmemchunks");
426 gmc = g_mem_chunk_new (
427 "memchunk", CHUNK_SIZE,
428 CHUNK_COUNT * CHUNK_SIZE,
430 for (i = 0; i < 1000000; i++) {
431 mem = g_mem_chunk_alloc (gmc);
434 g_mem_chunk_destroy (gmc);
435 time_end ("allocating 1000000 gmemchunks");
437 time_start ("Using malloc");
438 for (i = 0; i < 1000000; i++) {
441 time_end ("allocating 1000000 malloc");