1 /* GLIB sliced memory - fast threaded memory chunk allocator
2 * Copyright (C) 2005 Tim Janik
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 #define quick_rand32() (rand_accu = 1664525 * rand_accu + 1013904223, rand_accu)
25 static guint prime_size
= 1021; /* 769; 509 */
26 static gboolean clean_memchunks
= FALSE
;
27 static guint number_of_blocks
= 10000; /* total number of blocks allocated */
28 static guint number_of_repetitions
= 10000; /* number of alloc+free repetitions */
29 static gboolean want_corruption
= FALSE
;
31 /* --- old memchunk prototypes (memchunks.c) --- */
32 GMemChunk
* old_mem_chunk_new (const gchar
*name
,
36 void old_mem_chunk_destroy (GMemChunk
*mem_chunk
);
37 gpointer
old_mem_chunk_alloc (GMemChunk
*mem_chunk
);
38 gpointer
old_mem_chunk_alloc0 (GMemChunk
*mem_chunk
);
39 void old_mem_chunk_free (GMemChunk
*mem_chunk
,
41 void old_mem_chunk_clean (GMemChunk
*mem_chunk
);
42 void old_mem_chunk_reset (GMemChunk
*mem_chunk
);
43 void old_mem_chunk_print (GMemChunk
*mem_chunk
);
44 void old_mem_chunk_info (void);
45 #ifndef G_ALLOC_AND_FREE
46 #define G_ALLOC_AND_FREE 2
49 /* --- functions --- */
53 if (G_UNLIKELY (want_corruption
))
55 /* corruption per call likelyness is about 1:4000000 */
56 guint32 r
= g_random_int() % 8000009;
57 return r
== 277 ? +1 : r
== 281 ? -1 : 0;
62 static inline gpointer
63 memchunk_alloc (GMemChunk
**memchunkp
,
67 if (G_UNLIKELY (!*memchunkp
))
68 *memchunkp
= old_mem_chunk_new ("", size
, 4096, G_ALLOC_AND_FREE
);
69 return old_mem_chunk_alloc (*memchunkp
);
73 memchunk_free (GMemChunk
*memchunk
,
76 old_mem_chunk_free (memchunk
, chunk
);
78 old_mem_chunk_clean (memchunk
);
82 test_memchunk_thread (gpointer data
)
84 GMemChunk
**memchunks
;
88 guint32 rand_accu
= 2147483563;
89 /* initialize random numbers */
91 rand_accu
= *(guint32
*) data
;
95 g_get_current_time (&rand_tv
);
96 rand_accu
= rand_tv
.tv_usec
+ (rand_tv
.tv_sec
<< 16);
99 /* prepare for memchunk creation */
100 memchunks
= g_alloca (sizeof (memchunks
[0]) * prime_size
);
101 memset (memchunks
, 0, sizeof (memchunks
[0]) * prime_size
);
103 ps
= g_new (guint8
*, number_of_blocks
);
104 ss
= g_new (guint
, number_of_blocks
);
105 /* create number_of_blocks random sizes */
106 for (i
= 0; i
< number_of_blocks
; i
++)
107 ss
[i
] = quick_rand32() % prime_size
;
108 /* allocate number_of_blocks blocks */
109 for (i
= 0; i
< number_of_blocks
; i
++)
110 ps
[i
] = memchunk_alloc (&memchunks
[ss
[i
]], ss
[i
]);
111 for (j
= 0; j
< number_of_repetitions
; j
++)
113 /* free number_of_blocks/2 blocks */
114 for (i
= 0; i
< number_of_blocks
; i
+= 2)
115 memchunk_free (memchunks
[ss
[i
]], ps
[i
]);
116 /* allocate number_of_blocks/2 blocks with new sizes */
117 for (i
= 0; i
< number_of_blocks
; i
+= 2)
119 ss
[i
] = quick_rand32() % prime_size
;
120 ps
[i
] = memchunk_alloc (&memchunks
[ss
[i
]], ss
[i
]);
123 /* free number_of_blocks blocks */
124 for (i
= 0; i
< number_of_blocks
; i
++)
125 memchunk_free (memchunks
[ss
[i
]], ps
[i
]);
126 /* alloc and free many equally sized chunks in a row */
127 for (i
= 0; i
< number_of_repetitions
; i
++)
129 guint sz
= quick_rand32() % prime_size
;
130 guint k
= number_of_blocks
/ 100;
131 for (j
= 0; j
< k
; j
++)
132 ps
[j
] = memchunk_alloc (&memchunks
[sz
], sz
);
133 for (j
= 0; j
< k
; j
++)
134 memchunk_free (memchunks
[sz
], ps
[j
]);
136 /* cleanout memchunks */
137 for (i
= 0; i
< prime_size
; i
++)
139 old_mem_chunk_destroy (memchunks
[i
]);
147 test_sliced_mem_thread (gpointer data
)
149 guint32 rand_accu
= 2147483563;
154 /* initialize random numbers */
156 rand_accu
= *(guint32
*) data
;
160 g_get_current_time (&rand_tv
);
161 rand_accu
= rand_tv
.tv_usec
+ (rand_tv
.tv_sec
<< 16);
164 ps
= g_new (guint8
*, number_of_blocks
);
165 ss
= g_new (guint
, number_of_blocks
);
166 /* create number_of_blocks random sizes */
167 for (i
= 0; i
< number_of_blocks
; i
++)
168 ss
[i
] = quick_rand32() % prime_size
;
169 /* allocate number_of_blocks blocks */
170 for (i
= 0; i
< number_of_blocks
; i
++)
171 ps
[i
] = g_slice_alloc (ss
[i
] + corruption());
172 for (j
= 0; j
< number_of_repetitions
; j
++)
174 /* free number_of_blocks/2 blocks */
175 for (i
= 0; i
< number_of_blocks
; i
+= 2)
176 g_slice_free1 (ss
[i
] + corruption(), ps
[i
] + corruption());
177 /* allocate number_of_blocks/2 blocks with new sizes */
178 for (i
= 0; i
< number_of_blocks
; i
+= 2)
180 ss
[i
] = quick_rand32() % prime_size
;
181 ps
[i
] = g_slice_alloc (ss
[i
] + corruption());
184 /* free number_of_blocks blocks */
185 for (i
= 0; i
< number_of_blocks
; i
++)
186 g_slice_free1 (ss
[i
] + corruption(), ps
[i
] + corruption());
187 /* alloc and free many equally sized chunks in a row */
188 for (i
= 0; i
< number_of_repetitions
; i
++)
190 guint sz
= quick_rand32() % prime_size
;
191 guint k
= number_of_blocks
/ 100;
192 for (j
= 0; j
< k
; j
++)
193 ps
[j
] = g_slice_alloc (sz
+ corruption());
194 for (j
= 0; j
< k
; j
++)
195 g_slice_free1 (sz
+ corruption(), ps
[j
] + corruption());
206 g_print ("Usage: slice-test [n_threads] [G|S|M|O][f][c][~] [maxblocksize] [seed]\n");
213 guint seed32
, *seedp
= NULL
;
214 gboolean ccounters
= FALSE
, use_memchunks
= FALSE
;
216 const gchar
*mode
= "slab allocator + magazine cache", *emode
= " ";
218 n_threads
= g_ascii_strtoull (argv
[1], NULL
, 10);
221 guint i
, l
= strlen (argv
[2]);
222 for (i
= 0; i
< l
; i
++)
225 case 'G': /* GLib mode */
226 g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC
, FALSE
);
227 g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES
, FALSE
);
228 mode
= "slab allocator + magazine cache";
230 case 'S': /* slab mode */
231 g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC
, FALSE
);
232 g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES
, TRUE
);
233 mode
= "slab allocator";
235 case 'M': /* malloc mode */
236 g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC
, TRUE
);
237 mode
= "system malloc";
239 case 'O': /* old memchunks */
240 use_memchunks
= TRUE
;
241 mode
= "old memchunks";
243 case 'f': /* eager freeing */
244 g_slice_set_config (G_SLICE_CONFIG_WORKING_SET_MSECS
, 0);
245 clean_memchunks
= TRUE
;
246 emode
= " with eager freeing";
248 case 'c': /* print contention counters */
252 want_corruption
= TRUE
; /* force occasional corruption */
260 prime_size
= g_ascii_strtoull (argv
[3], NULL
, 10);
263 seed32
= g_ascii_strtoull (argv
[4], NULL
, 10);
267 g_thread_init (NULL
);
273 gchar strseed
[64] = "<random>";
278 g_snprintf (strseed
, 64, "%u", *seedp
);
279 g_print ("Starting %d threads allocating random blocks <= %u bytes with seed=%s using %s%s\n", n_threads
, prime_size
, strseed
, mode
, emode
);
281 threads
= g_alloca (sizeof(GThread
*) * n_threads
);
283 for (i
= 0; i
< n_threads
; i
++)
284 threads
[i
] = g_thread_create (test_sliced_mem_thread
, seedp
, TRUE
, NULL
);
287 for (i
= 0; i
< n_threads
; i
++)
288 threads
[i
] = g_thread_create (test_memchunk_thread
, seedp
, TRUE
, NULL
);
290 for (i
= 0; i
< n_threads
; i
++)
291 g_thread_join (threads
[i
]);
295 guint n
, n_chunks
= g_slice_get_config (G_SLICE_CONFIG_CHUNK_SIZES
);
296 g_print (" ChunkSize | MagazineSize | Contention\n");
297 for (i
= 0; i
< n_chunks
; i
++)
299 gint64
*vals
= g_slice_get_config_state (G_SLICE_CONFIG_CONTENTION_COUNTER
, i
, &n
);
300 g_print (" %9" G_GINT64_FORMAT
" | %9" G_GINT64_FORMAT
" | %9" G_GINT64_FORMAT
"\n", vals
[0], vals
[2], vals
[1]);