slice-test.c 9.63 KB
Newer Older
1 2 3 4 5 6
/* GLIB sliced memory - fast threaded memory chunk allocator
 * Copyright (C) 2005 Tim Janik
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * 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 10 11 12 13 14
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * 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 18 19 20 21 22
 */
#include <glib.h>

#include <stdio.h>
#include <string.h>

#define quick_rand32()  (rand_accu = 1664525 * rand_accu + 1013904223, rand_accu)
23
static guint    prime_size = 1021; /* 769; 509 */
24 25 26
static gboolean clean_memchunks = FALSE;
static guint    number_of_blocks = 10000;          /* total number of blocks allocated */
static guint    number_of_repetitions = 10000;     /* number of alloc+free repetitions */
27
static gboolean want_corruption = FALSE;
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

/* --- old memchunk prototypes (memchunks.c) --- */
GMemChunk*      old_mem_chunk_new       (const gchar  *name,
                                         gint          atom_size,
                                         gulong        area_size,
                                         gint          type);
void            old_mem_chunk_destroy   (GMemChunk *mem_chunk);
gpointer        old_mem_chunk_alloc     (GMemChunk *mem_chunk);
gpointer        old_mem_chunk_alloc0    (GMemChunk *mem_chunk);
void            old_mem_chunk_free      (GMemChunk *mem_chunk,
                                         gpointer   mem);
void            old_mem_chunk_clean     (GMemChunk *mem_chunk);
void            old_mem_chunk_reset     (GMemChunk *mem_chunk);
void            old_mem_chunk_print     (GMemChunk *mem_chunk);
void            old_mem_chunk_info      (void);
#ifndef G_ALLOC_AND_FREE
#define G_ALLOC_AND_FREE  2
#endif

/* --- functions --- */
48 49 50 51 52 53 54 55 56 57 58 59
static inline int
corruption (void)
{
  if (G_UNLIKELY (want_corruption))
    {
      /* corruption per call likelyness is about 1:4000000 */
      guint32 r = g_random_int() % 8000009;
      return r == 277 ? +1 : r == 281 ? -1 : 0;
    }
  return 0;
}

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
static inline gpointer
memchunk_alloc (GMemChunk **memchunkp,
                guint       size)
{
  size = MAX (size, 1);
  if (G_UNLIKELY (!*memchunkp))
    *memchunkp = old_mem_chunk_new ("", size, 4096, G_ALLOC_AND_FREE);
  return old_mem_chunk_alloc (*memchunkp);
}

static inline void
memchunk_free (GMemChunk *memchunk,
               gpointer   chunk)
{
  old_mem_chunk_free (memchunk, chunk);
  if (clean_memchunks)
    old_mem_chunk_clean (memchunk);
}

static gpointer
test_memchunk_thread (gpointer data)
{
82 83 84 85
  GMemChunk **memchunks;
  guint i, j;
  guint8 **ps;
  guint   *ss;
86 87 88 89 90 91
  guint32 rand_accu = 2147483563;
  /* initialize random numbers */
  if (data)
    rand_accu = *(guint32*) data;
  else
    {
92 93
      GTimeVal rand_tv;
      g_get_current_time (&rand_tv);
94 95 96 97
      rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16);
    }

  /* prepare for memchunk creation */
98
  memchunks = g_alloca (sizeof (memchunks[0]) * prime_size);
99 100
  memset (memchunks, 0, sizeof (memchunks[0]) * prime_size);

101 102
  ps = g_new (guint8*, number_of_blocks);
  ss = g_new (guint, number_of_blocks);
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  /* create number_of_blocks random sizes */
  for (i = 0; i < number_of_blocks; i++)
    ss[i] = quick_rand32() % prime_size;
  /* allocate number_of_blocks blocks */
  for (i = 0; i < number_of_blocks; i++)
    ps[i] = memchunk_alloc (&memchunks[ss[i]], ss[i]);
  for (j = 0; j < number_of_repetitions; j++)
    {
      /* free number_of_blocks/2 blocks */
      for (i = 0; i < number_of_blocks; i += 2)
        memchunk_free (memchunks[ss[i]], ps[i]);
      /* allocate number_of_blocks/2 blocks with new sizes */
      for (i = 0; i < number_of_blocks; i += 2)
        {
          ss[i] = quick_rand32() % prime_size;
          ps[i] = memchunk_alloc (&memchunks[ss[i]], ss[i]);
        }
    }
  /* free number_of_blocks blocks */
  for (i = 0; i < number_of_blocks; i++)
    memchunk_free (memchunks[ss[i]], ps[i]);
  /* alloc and free many equally sized chunks in a row */
  for (i = 0; i < number_of_repetitions; i++)
    {
      guint sz = quick_rand32() % prime_size;
      guint k = number_of_blocks / 100;
      for (j = 0; j < k; j++)
        ps[j] = memchunk_alloc (&memchunks[sz], sz);
      for (j = 0; j < k; j++)
        memchunk_free (memchunks[sz], ps[j]);
    }
  /* cleanout memchunks */
  for (i = 0; i < prime_size; i++)
    if (memchunks[i])
      old_mem_chunk_destroy (memchunks[i]);
138 139
  g_free (ps);
  g_free (ss);
140 141 142

  return NULL;
}
143 144 145 146 147

static gpointer
test_sliced_mem_thread (gpointer data)
{
  guint32 rand_accu = 2147483563;
148 149 150 151
  guint i, j;
  guint8 **ps;
  guint   *ss;

152 153 154 155 156
  /* initialize random numbers */
  if (data)
    rand_accu = *(guint32*) data;
  else
    {
157 158
      GTimeVal rand_tv;
      g_get_current_time (&rand_tv);
159 160 161
      rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16);
    }

162 163
  ps = g_new (guint8*, number_of_blocks);
  ss = g_new (guint, number_of_blocks);
164 165
  /* create number_of_blocks random sizes */
  for (i = 0; i < number_of_blocks; i++)
166
    ss[i] = quick_rand32() % prime_size;
167 168
  /* allocate number_of_blocks blocks */
  for (i = 0; i < number_of_blocks; i++)
169
    ps[i] = g_slice_alloc (ss[i] + corruption());
170
  for (j = 0; j < number_of_repetitions; j++)
171
    {
172 173
      /* free number_of_blocks/2 blocks */
      for (i = 0; i < number_of_blocks; i += 2)
174
        g_slice_free1 (ss[i] + corruption(), ps[i] + corruption());
175 176
      /* allocate number_of_blocks/2 blocks with new sizes */
      for (i = 0; i < number_of_blocks; i += 2)
177 178
        {
          ss[i] = quick_rand32() % prime_size;
179
          ps[i] = g_slice_alloc (ss[i] + corruption());
180 181
        }
    }
182 183
  /* free number_of_blocks blocks */
  for (i = 0; i < number_of_blocks; i++)
184
    g_slice_free1 (ss[i] + corruption(), 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 (sz + corruption());
192
      for (j = 0; j < k; j++)
193
        g_slice_free1 (sz + corruption(), ps[j] + corruption());
194
    }
195 196
  g_free (ps);
  g_free (ss);
197 198 199 200 201 202 203

  return NULL;
}

static void
usage (void)
{
204
  g_print ("Usage: slice-test [n_threads] [G|S|M|O][f][c][~] [maxblocksize] [seed]\n");
205 206 207 208 209 210 211
}

int
main (int   argc,
      char *argv[])
{
  guint seed32, *seedp = NULL;
212
  gboolean ccounters = FALSE, use_memchunks = FALSE;
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
  guint n_threads = 1;
  const gchar *mode = "slab allocator + magazine cache", *emode = " ";
  if (argc > 1)
    n_threads = g_ascii_strtoull (argv[1], NULL, 10);
  if (argc > 2)
    {
      guint i, l = strlen (argv[2]);
      for (i = 0; i < l; i++)
        switch (argv[2][i])
          {
          case 'G': /* GLib mode */
            g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
            g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, FALSE);
            mode = "slab allocator + magazine cache";
            break;
          case 'S': /* slab mode */
            g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
            g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, TRUE);
            mode = "slab allocator";
            break;
          case 'M': /* malloc mode */
            g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
            mode = "system malloc";
            break;
237 238 239 240
          case 'O': /* old memchunks */
            use_memchunks = TRUE;
            mode = "old memchunks";
            break;
241
          case 'f': /* eager freeing */
242
            g_slice_set_config (G_SLICE_CONFIG_WORKING_SET_MSECS, 0);
243
            clean_memchunks = TRUE;
244 245 246 247 248
            emode = " with eager freeing";
            break;
          case 'c': /* print contention counters */
            ccounters = TRUE;
            break;
249 250 251
          case '~':
            want_corruption = TRUE; /* force occasional corruption */
            break;
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
          default:
            usage();
            return 1;
          }
    }
  if (argc > 3)
    prime_size = g_ascii_strtoull (argv[3], NULL, 10);
  if (argc > 4)
    {
      seed32 = g_ascii_strtoull (argv[4], NULL, 10);
      seedp = &seed32;
    }

  if (argc <= 1)
    usage();

268 269 270 271 272 273 274 275
  {
    gchar strseed[64] = "<random>";
    GThread **threads;
    guint i;
    
    if (seedp)
      g_snprintf (strseed, 64, "%u", *seedp);
    g_print ("Starting %d threads allocating random blocks <= %u bytes with seed=%s using %s%s\n", n_threads, prime_size, strseed, mode, emode);
276
  
277 278
    threads = g_alloca (sizeof(GThread*) * n_threads);
    if (!use_memchunks)
279
      for (i = 0; i < n_threads; i++)
280
        threads[i] = g_thread_create (test_sliced_mem_thread, seedp, TRUE, NULL);
281 282 283
    else
      {
        for (i = 0; i < n_threads; i++)
284
          threads[i] = g_thread_create (test_memchunk_thread, seedp, TRUE, NULL);
285 286 287
      }
    for (i = 0; i < n_threads; i++)
      g_thread_join (threads[i]);
288
  
289 290 291 292 293 294 295
    if (ccounters)
      {
        guint n, n_chunks = g_slice_get_config (G_SLICE_CONFIG_CHUNK_SIZES);
        g_print ("    ChunkSize | MagazineSize | Contention\n");
        for (i = 0; i < n_chunks; i++)
          {
            gint64 *vals = g_slice_get_config_state (G_SLICE_CONFIG_CONTENTION_COUNTER, i, &n);
Dan Winship's avatar
Dan Winship committed
296
            g_print ("  %9" G_GINT64_FORMAT "   |  %9" G_GINT64_FORMAT "   |  %9" G_GINT64_FORMAT "\n", vals[0], vals[2], vals[1]);
297 298 299 300 301 302 303
            g_free (vals);
          }
      }
    else
      g_print ("Done.\n");
    return 0;
  }
304
}