1/* GLIB sliced memory - fast threaded memory chunk allocator
2 * Copyright (C) 2005 Tim Janik
3 *
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.1 of the License, or (at your option) any later version.
8 *
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.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 */
17#include <glib.h>
18
19#include <stdio.h>
20#include <string.h>
21
22#define quick_rand32() (rand_accu = 1664525 * rand_accu + 1013904223, rand_accu)
23static guint prime_size = 1021; /* 769; 509 */
24static gboolean clean_memchunks = FALSE;
25static guint number_of_blocks = 10000; /* total number of blocks allocated */
26static guint number_of_repetitions = 10000; /* number of alloc+free repetitions */
27static gboolean want_corruption = FALSE;
28
29/* --- old memchunk prototypes (memchunks.c) --- */
30GMemChunk* old_mem_chunk_new (const gchar *name,
31 gint atom_size,
32 gulong area_size,
33 gint type);
34void old_mem_chunk_destroy (GMemChunk *mem_chunk);
35gpointer old_mem_chunk_alloc (GMemChunk *mem_chunk);
36gpointer old_mem_chunk_alloc0 (GMemChunk *mem_chunk);
37void old_mem_chunk_free (GMemChunk *mem_chunk,
38 gpointer mem);
39void old_mem_chunk_clean (GMemChunk *mem_chunk);
40void old_mem_chunk_reset (GMemChunk *mem_chunk);
41void old_mem_chunk_print (GMemChunk *mem_chunk);
42void old_mem_chunk_info (void);
43#ifndef G_ALLOC_AND_FREE
44#define G_ALLOC_AND_FREE 2
45#endif
46
47/* --- functions --- */
48static inline int
49corruption (void)
50{
51 if (G_UNLIKELY (want_corruption))
52 {
53 /* corruption per call likelyness is about 1:4000000 */
54 guint32 r = g_random_int() % 8000009;
55 return r == 277 ? +1 : r == 281 ? -1 : 0;
56 }
57 return 0;
58}
59
60static inline gpointer
61memchunk_alloc (GMemChunk **memchunkp,
62 guint size)
63{
64 size = MAX (size, 1);
65 if (G_UNLIKELY (!*memchunkp))
66 *memchunkp = old_mem_chunk_new (name: "", atom_size: size, area_size: 4096, G_ALLOC_AND_FREE);
67 return old_mem_chunk_alloc (mem_chunk: *memchunkp);
68}
69
70static inline void
71memchunk_free (GMemChunk *memchunk,
72 gpointer chunk)
73{
74 old_mem_chunk_free (mem_chunk: memchunk, mem: chunk);
75 if (clean_memchunks)
76 old_mem_chunk_clean (mem_chunk: memchunk);
77}
78
79static gpointer
80test_memchunk_thread (gpointer data)
81{
82 GMemChunk **memchunks;
83 guint i, j;
84 guint8 **ps;
85 guint *ss;
86 guint32 rand_accu = 2147483563;
87 /* initialize random numbers */
88 if (data)
89 rand_accu = *(guint32*) data;
90 else
91 {
92 GTimeVal rand_tv;
93 g_get_current_time (result: &rand_tv);
94 rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16);
95 }
96
97 /* prepare for memchunk creation */
98 memchunks = g_alloca (sizeof (memchunks[0]) * prime_size);
99 memset (s: memchunks, c: 0, n: sizeof (memchunks[0]) * prime_size);
100
101 ps = g_new (guint8*, number_of_blocks);
102 ss = g_new (guint, number_of_blocks);
103 /* create number_of_blocks random sizes */
104 for (i = 0; i < number_of_blocks; i++)
105 ss[i] = quick_rand32() % prime_size;
106 /* allocate number_of_blocks blocks */
107 for (i = 0; i < number_of_blocks; i++)
108 ps[i] = memchunk_alloc (memchunkp: &memchunks[ss[i]], size: ss[i]);
109 for (j = 0; j < number_of_repetitions; j++)
110 {
111 /* free number_of_blocks/2 blocks */
112 for (i = 0; i < number_of_blocks; i += 2)
113 memchunk_free (memchunk: memchunks[ss[i]], chunk: ps[i]);
114 /* allocate number_of_blocks/2 blocks with new sizes */
115 for (i = 0; i < number_of_blocks; i += 2)
116 {
117 ss[i] = quick_rand32() % prime_size;
118 ps[i] = memchunk_alloc (memchunkp: &memchunks[ss[i]], size: ss[i]);
119 }
120 }
121 /* free number_of_blocks blocks */
122 for (i = 0; i < number_of_blocks; i++)
123 memchunk_free (memchunk: memchunks[ss[i]], chunk: ps[i]);
124 /* alloc and free many equally sized chunks in a row */
125 for (i = 0; i < number_of_repetitions; i++)
126 {
127 guint sz = quick_rand32() % prime_size;
128 guint k = number_of_blocks / 100;
129 for (j = 0; j < k; j++)
130 ps[j] = memchunk_alloc (memchunkp: &memchunks[sz], size: sz);
131 for (j = 0; j < k; j++)
132 memchunk_free (memchunk: memchunks[sz], chunk: ps[j]);
133 }
134 /* cleanout memchunks */
135 for (i = 0; i < prime_size; i++)
136 if (memchunks[i])
137 old_mem_chunk_destroy (mem_chunk: memchunks[i]);
138 g_free (mem: ps);
139 g_free (mem: ss);
140
141 return NULL;
142}
143
144static gpointer
145test_sliced_mem_thread (gpointer data)
146{
147 guint32 rand_accu = 2147483563;
148 guint i, j;
149 guint8 **ps;
150 guint *ss;
151
152 /* initialize random numbers */
153 if (data)
154 rand_accu = *(guint32*) data;
155 else
156 {
157 GTimeVal rand_tv;
158 g_get_current_time (result: &rand_tv);
159 rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16);
160 }
161
162 ps = g_new (guint8*, number_of_blocks);
163 ss = g_new (guint, number_of_blocks);
164 /* create number_of_blocks random sizes */
165 for (i = 0; i < number_of_blocks; i++)
166 ss[i] = quick_rand32() % prime_size;
167 /* allocate number_of_blocks blocks */
168 for (i = 0; i < number_of_blocks; i++)
169 ps[i] = g_slice_alloc (block_size: ss[i] + corruption());
170 for (j = 0; j < number_of_repetitions; j++)
171 {
172 /* free number_of_blocks/2 blocks */
173 for (i = 0; i < number_of_blocks; i += 2)
174 g_slice_free1 (block_size: ss[i] + corruption(), mem_block: ps[i] + corruption());
175 /* allocate number_of_blocks/2 blocks with new sizes */
176 for (i = 0; i < number_of_blocks; i += 2)
177 {
178 ss[i] = quick_rand32() % prime_size;
179 ps[i] = g_slice_alloc (block_size: ss[i] + corruption());
180 }
181 }
182 /* free number_of_blocks blocks */
183 for (i = 0; i < number_of_blocks; i++)
184 g_slice_free1 (block_size: ss[i] + corruption(), mem_block: ps[i] + corruption());
185 /* alloc and free many equally sized chunks in a row */
186 for (i = 0; i < number_of_repetitions; i++)
187 {
188 guint sz = quick_rand32() % prime_size;
189 guint k = number_of_blocks / 100;
190 for (j = 0; j < k; j++)
191 ps[j] = g_slice_alloc (block_size: sz + corruption());
192 for (j = 0; j < k; j++)
193 g_slice_free1 (block_size: sz + corruption(), mem_block: ps[j] + corruption());
194 }
195 g_free (mem: ps);
196 g_free (mem: ss);
197
198 return NULL;
199}
200
201static void
202usage (void)
203{
204 g_print (format: "Usage: slice-test [n_threads] [G|S|M|O][f][c][~] [maxblocksize] [seed]\n");
205}
206
207int
208main (int argc,
209 char *argv[])
210{
211 guint seed32, *seedp = NULL;
212 gboolean ccounters = FALSE, use_memchunks = FALSE;
213 guint n_threads = 1;
214 const gchar *mode = "slab allocator + magazine cache", *emode = " ";
215 if (argc > 1)
216 n_threads = g_ascii_strtoull (nptr: argv[1], NULL, base: 10);
217 if (argc > 2)
218 {
219 guint i, l = strlen (s: argv[2]);
220 for (i = 0; i < l; i++)
221 switch (argv[2][i])
222 {
223 case 'G': /* GLib mode */
224 g_slice_set_config (ckey: G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
225 g_slice_set_config (ckey: G_SLICE_CONFIG_BYPASS_MAGAZINES, FALSE);
226 mode = "slab allocator + magazine cache";
227 break;
228 case 'S': /* slab mode */
229 g_slice_set_config (ckey: G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
230 g_slice_set_config (ckey: G_SLICE_CONFIG_BYPASS_MAGAZINES, TRUE);
231 mode = "slab allocator";
232 break;
233 case 'M': /* malloc mode */
234 g_slice_set_config (ckey: G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
235 mode = "system malloc";
236 break;
237 case 'O': /* old memchunks */
238 use_memchunks = TRUE;
239 mode = "old memchunks";
240 break;
241 case 'f': /* eager freeing */
242 g_slice_set_config (ckey: G_SLICE_CONFIG_WORKING_SET_MSECS, value: 0);
243 clean_memchunks = TRUE;
244 emode = " with eager freeing";
245 break;
246 case 'c': /* print contention counters */
247 ccounters = TRUE;
248 break;
249 case '~':
250 want_corruption = TRUE; /* force occasional corruption */
251 break;
252 default:
253 usage();
254 return 1;
255 }
256 }
257 if (argc > 3)
258 prime_size = g_ascii_strtoull (nptr: argv[3], NULL, base: 10);
259 if (argc > 4)
260 {
261 seed32 = g_ascii_strtoull (nptr: argv[4], NULL, base: 10);
262 seedp = &seed32;
263 }
264
265 if (argc <= 1)
266 usage();
267
268 {
269 gchar strseed[64] = "<random>";
270 GThread **threads;
271 guint i;
272
273 if (seedp)
274 g_snprintf (string: strseed, n: 64, format: "%u", *seedp);
275 g_print (format: "Starting %d threads allocating random blocks <= %u bytes with seed=%s using %s%s\n", n_threads, prime_size, strseed, mode, emode);
276
277 threads = g_alloca (sizeof(GThread*) * n_threads);
278 if (!use_memchunks)
279 for (i = 0; i < n_threads; i++)
280 threads[i] = g_thread_create (func: test_sliced_mem_thread, data: seedp, TRUE, NULL);
281 else
282 {
283 for (i = 0; i < n_threads; i++)
284 threads[i] = g_thread_create (func: test_memchunk_thread, data: seedp, TRUE, NULL);
285 }
286 for (i = 0; i < n_threads; i++)
287 g_thread_join (thread: threads[i]);
288
289 if (ccounters)
290 {
291 guint n, n_chunks = g_slice_get_config (ckey: G_SLICE_CONFIG_CHUNK_SIZES);
292 g_print (format: " ChunkSize | MagazineSize | Contention\n");
293 for (i = 0; i < n_chunks; i++)
294 {
295 gint64 *vals = g_slice_get_config_state (ckey: G_SLICE_CONFIG_CONTENTION_COUNTER, address: i, n_values: &n);
296 g_print (format: " %9" G_GINT64_FORMAT " | %9" G_GINT64_FORMAT " | %9" G_GINT64_FORMAT "\n", vals[0], vals[2], vals[1]);
297 g_free (mem: vals);
298 }
299 }
300 else
301 g_print (format: "Done.\n");
302 return 0;
303 }
304}
305

source code of gtk/subprojects/glib/tests/slice-test.c