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
23 #include "camel-memchunk.h"
25 #include <string.h> /* memset() */
33 struct timeval timeit_start;
36 time_start (const gchar *desc)
38 gettimeofday (&timeit_start, NULL);
39 printf ("starting: %s\n", desc);
43 time_end (const gchar *desc)
48 gettimeofday (&end, NULL);
49 diff = end.tv_sec * 1000 + end.tv_usec / 1000;
50 diff -= timeit_start.tv_sec * 1000 + timeit_start.tv_usec / 1000;
52 "%s took %ld.%03ld seconds\n",
53 desc, diff / 1000, diff % 1000);
60 typedef struct _MemChunkFreeNode {
61 struct _MemChunkFreeNode *next;
70 struct _CamelMemChunk {
71 guint blocksize; /* number of atoms in a block */
72 guint atomsize; /* size of each atom */
73 GPtrArray *blocks; /* blocks of raw memory */
74 struct _MemChunkFreeNode *free;
79 * @atomcount: the number of atoms stored in a single malloc'd block of memory
80 * @atomsize: the size of each allocation
82 * Create a new #CamelMemChunk header. Memchunks are an efficient way to
83 * allocate and deallocate identical sized blocks of memory quickly, and
86 * camel_memchunks are effectively the same as gmemchunks, only faster (much),
87 * and they use less memory overhead for housekeeping.
89 * Returns: a new #CamelMemChunk
94 camel_memchunk_new (gint atomcount,
97 CamelMemChunk *memchunk = g_malloc (sizeof (*memchunk));
99 memchunk->blocksize = atomcount;
100 memchunk->atomsize = MAX (atomsize, sizeof (MemChunkFreeNode));
101 memchunk->blocks = g_ptr_array_new ();
102 memchunk->free = NULL;
108 * camel_memchunk_alloc:
109 * @memchunk: an #CamelMemChunk
111 * Allocate a new atom size block of memory from an #CamelMemChunk.
112 * Free the returned atom with camel_memchunk_free().
114 * Returns: an allocated block of memory
119 camel_memchunk_alloc (CamelMemChunk *memchunk)
129 mem = ((gchar *) f) + (f->atoms * memchunk->atomsize);
132 memchunk->free = memchunk->free->next;
136 b = g_malloc (memchunk->blocksize * memchunk->atomsize);
137 g_ptr_array_add (memchunk->blocks, b);
138 f = (MemChunkFreeNode *) &b[memchunk->atomsize];
139 f->atoms = memchunk->blocksize - 1;
147 * camel_memchunk_alloc0:
148 * @memchunk: an #CamelMemChunk
150 * Allocate a new atom size block of memory from an #CamelMemChunk,
151 * and fill the memory with zeros. Free the returned atom with
152 * camel_memchunk_free().
154 * Returns: an allocated block of memory
159 camel_memchunk_alloc0 (CamelMemChunk *memchunk)
163 mem = camel_memchunk_alloc (memchunk);
164 memset (mem, 0, memchunk->atomsize);
170 * camel_memchunk_free:
171 * @memchunk: an #CamelMemChunk
172 * @mem: address of atom to free
174 * Free a single atom back to the free pool of atoms in the given
180 camel_memchunk_free (CamelMemChunk *memchunk,
185 /* Put the location back in the free list. If we knew if the
186 * preceeding or following cells were free, we could merge the
187 * free nodes, but it doesn't really add much. */
189 f->next = memchunk->free;
193 /* We could store the free list sorted - we could then do the above,
194 * and also probably improve the locality of reference properties for
195 * the allocator. (And it would simplify some other algorithms at
196 * that, but slow this one down significantly.) */
200 * camel_memchunk_empty:
201 * @memchunk: an #CamelMemChunk
203 * Clean out the memchunk buffers. Marks all allocated memory as free blocks,
204 * but does not give it back to the system. Can be used if the memchunk
205 * is to be used repeatedly.
210 camel_memchunk_empty (CamelMemChunk *memchunk)
212 MemChunkFreeNode *f, *h = NULL;
215 for (i = 0; i < memchunk->blocks->len; i++) {
216 f = (MemChunkFreeNode *) memchunk->blocks->pdata[i];
217 f->atoms = memchunk->blocksize;
226 struct _cleaninfo *next;
229 gint size; /* just so tree_search has it, sigh */
233 tree_compare (struct _cleaninfo *a,
234 struct _cleaninfo *b)
236 if (a->base < b->base)
238 else if (a->base > b->base)
244 tree_search (struct _cleaninfo *a,
247 if (a->base <= mem) {
248 if (mem < &a->base[a->size])
256 * camel_memchunk_clean:
257 * @memchunk: an #CamelMemChunk
259 * Scan all empty blocks and check for blocks which can be free'd
260 * back to the system.
262 * This routine may take a while to run if there are many allocated
263 * memory blocks (if the total number of allocations is many times
264 * greater than atomcount).
269 camel_memchunk_clean (CamelMemChunk *memchunk)
274 struct _cleaninfo *ci, *hi = NULL;
277 if (memchunk->blocks->len == 0 || f == NULL)
280 /* first, setup the tree/list so we can map free block addresses to block addresses */
281 tree = g_tree_new ((GCompareFunc) tree_compare);
282 for (i = 0; i < memchunk->blocks->len; i++) {
283 ci = alloca (sizeof (*ci));
285 ci->base = memchunk->blocks->pdata[i];
286 ci->size = memchunk->blocksize * memchunk->atomsize;
287 g_tree_insert (tree, ci, ci);
292 /* now, scan all free nodes, and count them in their tree node */
294 ci = g_tree_search (tree, (GCompareFunc) tree_search, f);
296 ci->count += f->atoms;
298 g_warning ("error, can't find free node in memory block\n");
303 /* if any nodes are all free, free & unlink them */
306 if (ci->count == memchunk->blocksize) {
307 MemChunkFreeNode *prev = NULL;
311 if (tree_search (ci, (gpointer) f) == 0) {
312 /* prune this node from our free-node list */
314 prev->next = f->next;
316 memchunk->free = f->next;
324 g_ptr_array_remove_fast (memchunk->blocks, ci->base);
330 g_tree_destroy (tree);
334 * camel_memchunk_destroy:
335 * @memchunk: an #CamelMemChunk
337 * Free the memchunk header, and all associated memory.
342 camel_memchunk_destroy (CamelMemChunk *memchunk)
346 if (memchunk == NULL)
349 for (i = 0; i < memchunk->blocks->len; i++)
350 g_free (memchunk->blocks->pdata[i]);
352 g_ptr_array_free (memchunk->blocks, TRUE);
359 #define CHUNK_SIZE (20)
360 #define CHUNK_COUNT (32)
373 s = strv_set (s, 1, "Testing 1");
374 s = strv_set (s, 2, "Testing 2");
375 s = strv_set (s, 3, "Testing 3");
376 s = strv_set (s, 4, "Testing 4");
377 s = strv_set (s, 5, "Testing 5");
378 s = strv_set (s, 6, "Testing 7");
380 for (i = 0; i < 8; i++) {
381 printf ("s[%d] = %s\n", i, strv_get (s, i));
386 printf ("packing ...\n");
389 for (i = 0; i < 8; i++) {
390 printf ("s[%d] = %s\n", i, strv_get (s, i));
393 printf ("setting ...\n");
395 s = strv_set_ref (s, 1, "Testing 1 x");
397 for (i = 0; i < 8; i++) {
398 printf ("s[%d] = %s\n", i, strv_get (s, i));
401 printf ("packing ...\n");
404 for (i = 0; i < 8; i++) {
405 printf ("s[%d] = %s\n", i, strv_get (s, i));
411 time_start ("Using memchunks");
412 mc = memchunk_new (CHUNK_COUNT, CHUNK_SIZE);
413 for (i = 0; i < 1000000; i++) {
414 mem = memchunk_alloc (mc);
416 memchunk_free (mc, mem);
419 memchunk_destroy (mc);
420 time_end ("allocating 1000000 memchunks, freeing 500k");
422 time_start ("Using gmemchunks");
423 gmc = g_mem_chunk_new ("memchunk", CHUNK_SIZE, CHUNK_SIZE * CHUNK_COUNT, G_ALLOC_AND_FREE);
424 for (i = 0; i < 1000000; i++) {
425 mem = g_mem_chunk_alloc (gmc);
427 g_mem_chunk_free (gmc, mem);
430 g_mem_chunk_destroy (gmc);
431 time_end ("allocating 1000000 gmemchunks, freeing 500k");
433 time_start ("Using memchunks");
434 mc = memchunk_new (CHUNK_COUNT, CHUNK_SIZE);
435 for (i = 0; i < 1000000; i++) {
436 mem = memchunk_alloc (mc);
439 memchunk_destroy (mc);
440 time_end ("allocating 1000000 memchunks");
442 time_start ("Using gmemchunks");
443 gmc = g_mem_chunk_new ("memchunk", CHUNK_SIZE, CHUNK_COUNT * CHUNK_SIZE, G_ALLOC_ONLY);
444 for (i = 0; i < 1000000; i++) {
445 mem = g_mem_chunk_alloc (gmc);
448 g_mem_chunk_destroy (gmc);
449 time_end ("allocating 1000000 gmemchunks");
451 time_start ("Using malloc");
452 for (i = 0; i < 1000000; i++) {
455 time_end ("allocating 1000000 malloc");