sl@0: /* GLIB sliced memory - fast threaded memory chunk allocator sl@0: * Copyright (C) 2005 Tim Janik sl@0: * Portion Copyright © 2008-09 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. sl@0: * This library is free software; you can redistribute it and/or sl@0: * modify it under the terms of the GNU Lesser General Public sl@0: * License as published by the Free Software Foundation; either sl@0: * version 2 of the License, or (at your option) any later version. sl@0: * sl@0: * This library is distributed in the hope that it will be useful, sl@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of sl@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU sl@0: * Lesser General Public License for more details. sl@0: * sl@0: * You should have received a copy of the GNU Lesser General Public sl@0: * License along with this library; if not, write to the sl@0: * Free Software Foundation, Inc., 59 Temple Place - Suite 330, sl@0: * Boston, MA 02111-1307, USA. sl@0: */ sl@0: #include sl@0: sl@0: #include sl@0: #include sl@0: #ifdef __SYMBIAN32__ sl@0: #include "mrt2_glib2_test.h" sl@0: #endif /*__SYMBIAN32__*/ sl@0: #define quick_rand32() (rand_accu = 1664525 * rand_accu + 1013904223, rand_accu) sl@0: static guint prime_size = 1021; // 769; // 509 sl@0: static gboolean clean_memchunks = FALSE; sl@0: static guint number_of_blocks = 100; /* total number of blocks allocated */ sl@0: static guint number_of_repetitions = 100; /* number of alloc+free repetitions */ sl@0: static gboolean want_corruption = FALSE; sl@0: sl@0: /* --- old memchunk prototypes (memchunks.c) --- */ sl@0: void old_mem_chunks_init (void); sl@0: GMemChunk* old_mem_chunk_new (const gchar *name, sl@0: gint atom_size, sl@0: gulong area_size, sl@0: gint type); sl@0: void old_mem_chunk_destroy (GMemChunk *mem_chunk); sl@0: gpointer old_mem_chunk_alloc (GMemChunk *mem_chunk); sl@0: gpointer old_mem_chunk_alloc0 (GMemChunk *mem_chunk); sl@0: void old_mem_chunk_free (GMemChunk *mem_chunk, sl@0: gpointer mem); sl@0: void old_mem_chunk_clean (GMemChunk *mem_chunk); sl@0: void old_mem_chunk_reset (GMemChunk *mem_chunk); sl@0: void old_mem_chunk_print (GMemChunk *mem_chunk); sl@0: void old_mem_chunk_info (void); sl@0: #ifndef G_ALLOC_AND_FREE sl@0: #define G_ALLOC_AND_FREE 2 sl@0: #endif sl@0: sl@0: /* --- functions --- */ sl@0: static inline int sl@0: corruption (void) sl@0: { sl@0: if (G_UNLIKELY (want_corruption)) sl@0: { sl@0: /* corruption per call likelyness is about 1:4000000 */ sl@0: guint32 r = g_random_int() % 8000009; sl@0: return r == 277 ? +1 : r == 281 ? -1 : 0; sl@0: } sl@0: return 0; sl@0: } sl@0: sl@0: static inline gpointer sl@0: memchunk_alloc (GMemChunk **memchunkp, sl@0: guint size) sl@0: { sl@0: size = MAX (size, 1); sl@0: if (G_UNLIKELY (!*memchunkp)) sl@0: *memchunkp = old_mem_chunk_new ("", size, 4096, G_ALLOC_AND_FREE); sl@0: return old_mem_chunk_alloc (*memchunkp); sl@0: } sl@0: sl@0: static inline void sl@0: memchunk_free (GMemChunk *memchunk, sl@0: gpointer chunk) sl@0: { sl@0: old_mem_chunk_free (memchunk, chunk); sl@0: if (clean_memchunks) sl@0: old_mem_chunk_clean (memchunk); sl@0: } sl@0: sl@0: static gpointer sl@0: test_memchunk_thread (gpointer data) sl@0: { sl@0: GMemChunk **memchunks; sl@0: guint i, j; sl@0: guint8 **ps; sl@0: guint *ss; sl@0: guint32 rand_accu = 2147483563; sl@0: /* initialize random numbers */ sl@0: if (data) sl@0: rand_accu = *(guint32*) data; sl@0: else sl@0: { sl@0: GTimeVal rand_tv; sl@0: g_get_current_time (&rand_tv); sl@0: rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16); sl@0: } sl@0: sl@0: /* prepare for memchunk creation */ sl@0: memchunks = (GMemChunk **)g_alloca (sizeof (memchunks[0]) * prime_size); sl@0: memset (memchunks, 0, sizeof (memchunks[0]) * prime_size); sl@0: sl@0: ps = g_new (guint8*, number_of_blocks); sl@0: ss = g_new (guint, number_of_blocks); sl@0: /* create number_of_blocks random sizes */ sl@0: for (i = 0; i < number_of_blocks; i++) sl@0: ss[i] = quick_rand32() % prime_size; sl@0: /* allocate number_of_blocks blocks */ sl@0: for (i = 0; i < number_of_blocks; i++) sl@0: ps[i] = memchunk_alloc (&memchunks[ss[i]], ss[i]); sl@0: for (j = 0; j < number_of_repetitions; j++) sl@0: { sl@0: /* free number_of_blocks/2 blocks */ sl@0: for (i = 0; i < number_of_blocks; i += 2) sl@0: memchunk_free (memchunks[ss[i]], ps[i]); sl@0: /* allocate number_of_blocks/2 blocks with new sizes */ sl@0: for (i = 0; i < number_of_blocks; i += 2) sl@0: { sl@0: ss[i] = quick_rand32() % prime_size; sl@0: ps[i] = memchunk_alloc (&memchunks[ss[i]], ss[i]); sl@0: } sl@0: } sl@0: /* free number_of_blocks blocks */ sl@0: for (i = 0; i < number_of_blocks; i++) sl@0: memchunk_free (memchunks[ss[i]], ps[i]); sl@0: /* alloc and free many equally sized chunks in a row */ sl@0: for (i = 0; i < number_of_repetitions; i++) sl@0: { sl@0: guint sz = quick_rand32() % prime_size; sl@0: guint k = number_of_blocks / 100; sl@0: for (j = 0; j < k; j++) sl@0: ps[j] = memchunk_alloc (&memchunks[sz], sz); sl@0: for (j = 0; j < k; j++) sl@0: memchunk_free (memchunks[sz], ps[j]); sl@0: } sl@0: /* cleanout memchunks */ sl@0: for (i = 0; i < prime_size; i++) sl@0: if (memchunks[i]) sl@0: old_mem_chunk_destroy (memchunks[i]); sl@0: g_free (ps); sl@0: g_free (ss); sl@0: sl@0: return NULL; sl@0: } sl@0: sl@0: static gpointer sl@0: test_sliced_mem_thread (gpointer data) sl@0: { sl@0: guint32 rand_accu = 2147483563; sl@0: guint i, j; sl@0: guint8 **ps; sl@0: guint *ss; sl@0: sl@0: /* initialize random numbers */ sl@0: if (data) sl@0: rand_accu = *(guint32*) data; sl@0: else sl@0: { sl@0: GTimeVal rand_tv; sl@0: g_get_current_time (&rand_tv); sl@0: rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16); sl@0: } sl@0: sl@0: ps = g_new (guint8*, number_of_blocks); sl@0: ss = g_new (guint, number_of_blocks); sl@0: /* create number_of_blocks random sizes */ sl@0: for (i = 0; i < number_of_blocks; i++) sl@0: ss[i] = quick_rand32() % prime_size; sl@0: /* allocate number_of_blocks blocks */ sl@0: for (i = 0; i < number_of_blocks; i++) sl@0: ps[i] = g_slice_alloc (ss[i] + corruption()); sl@0: for (j = 0; j < number_of_repetitions; j++) sl@0: { sl@0: /* free number_of_blocks/2 blocks */ sl@0: for (i = 0; i < number_of_blocks; i += 2) sl@0: g_slice_free1 (ss[i] + corruption(), ps[i] + corruption()); sl@0: /* allocate number_of_blocks/2 blocks with new sizes */ sl@0: for (i = 0; i < number_of_blocks; i += 2) sl@0: { sl@0: ss[i] = quick_rand32() % prime_size; sl@0: ps[i] = g_slice_alloc (ss[i] + corruption()); sl@0: } sl@0: } sl@0: /* free number_of_blocks blocks */ sl@0: for (i = 0; i < number_of_blocks; i++) sl@0: g_slice_free1 (ss[i] + corruption(), ps[i] + corruption()); sl@0: /* alloc and free many equally sized chunks in a row */ sl@0: for (i = 0; i < number_of_repetitions; i++) sl@0: { sl@0: guint sz = quick_rand32() % prime_size; sl@0: guint k = number_of_blocks / 100; sl@0: for (j = 0; j < k; j++) sl@0: ps[j] = g_slice_alloc (sz + corruption()); sl@0: for (j = 0; j < k; j++) sl@0: g_slice_free1 (sz + corruption(), ps[j] + corruption()); sl@0: } sl@0: g_free (ps); sl@0: g_free (ss); sl@0: sl@0: return NULL; sl@0: } sl@0: sl@0: static void sl@0: usage (void) sl@0: { sl@0: g_print ("Usage: slice-test [n_threads] [G|S|M|O][f][c][~] [maxblocksize] [seed]\n"); sl@0: } sl@0: sl@0: int sl@0: main (int argc, sl@0: char *argv[]) sl@0: { sl@0: guint seed32, *seedp = NULL; sl@0: gboolean ccounters = FALSE, use_memchunks = FALSE; sl@0: guint n_threads = 1; sl@0: const gchar *mode = "slab allocator + magazine cache", *emode = " "; sl@0: #ifdef __SYMBIAN32__ sl@0: g_log_set_handler (NULL, G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG, &mrtLogHandler, NULL); sl@0: g_set_print_handler(mrtPrintHandler); sl@0: #endif /*__SYMBIAN32__*/ sl@0: if (argc > 1) sl@0: n_threads = g_ascii_strtoull (argv[1], NULL, 10); sl@0: if (argc > 2) sl@0: { sl@0: guint i, l = strlen (argv[2]); sl@0: for (i = 0; i < l; i++) sl@0: switch (argv[2][i]) sl@0: { sl@0: case 'G': /* GLib mode */ sl@0: g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE); sl@0: g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, FALSE); sl@0: mode = "slab allocator + magazine cache"; sl@0: break; sl@0: case 'S': /* slab mode */ sl@0: g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE); sl@0: g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, TRUE); sl@0: mode = "slab allocator"; sl@0: break; sl@0: case 'M': /* malloc mode */ sl@0: g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE); sl@0: mode = "system malloc"; sl@0: break; sl@0: case 'O': /* old memchunks */ sl@0: use_memchunks = TRUE; sl@0: mode = "old memchunks"; sl@0: break; sl@0: case 'f': /* eager freeing */ sl@0: g_slice_set_config (G_SLICE_CONFIG_WORKING_SET_MSECS, 0); sl@0: clean_memchunks = TRUE; sl@0: emode = " with eager freeing"; sl@0: break; sl@0: case 'c': /* print contention counters */ sl@0: ccounters = TRUE; sl@0: break; sl@0: case '~': sl@0: want_corruption = TRUE; /* force occasional corruption */ sl@0: break; sl@0: default: sl@0: usage(); sl@0: return 1; sl@0: } sl@0: } sl@0: if (argc > 3) sl@0: prime_size = g_ascii_strtoull (argv[3], NULL, 10); sl@0: if (argc > 4) sl@0: { sl@0: seed32 = g_ascii_strtoull (argv[4], NULL, 10); sl@0: seedp = &seed32; sl@0: } sl@0: sl@0: g_thread_init (NULL); sl@0: sl@0: if (argc <= 1) sl@0: usage(); sl@0: sl@0: { sl@0: gchar strseed[64] = ""; sl@0: GThread **threads; sl@0: guint i; sl@0: sl@0: if (seedp) sl@0: g_snprintf (strseed, 64, "%u", *seedp); sl@0: g_print ("Starting %d threads allocating random blocks <= %u bytes with seed=%s using %s%s\n", n_threads, prime_size, strseed, mode, emode); sl@0: threads = (GThread **)g_alloca (sizeof(GThread*) * n_threads); sl@0: if (!use_memchunks) sl@0: for (i = 0; i < n_threads; i++) sl@0: threads[i] = g_thread_create_full (test_sliced_mem_thread, seedp, 0, TRUE, FALSE, 0, NULL); sl@0: else sl@0: { sl@0: old_mem_chunks_init(); sl@0: for (i = 0; i < n_threads; i++) sl@0: threads[i] = g_thread_create_full (test_memchunk_thread, seedp, 0, TRUE, FALSE, 0, NULL); sl@0: } sl@0: for (i = 0; i < n_threads; i++) sl@0: g_thread_join (threads[i]); sl@0: sl@0: if (ccounters) sl@0: { sl@0: guint n, n_chunks = g_slice_get_config (G_SLICE_CONFIG_CHUNK_SIZES); sl@0: g_print (" ChunkSize | MagazineSize | Contention\n"); sl@0: for (i = 0; i < n_chunks; i++) sl@0: { sl@0: gint64 *vals = g_slice_get_config_state (G_SLICE_CONFIG_CONTENTION_COUNTER, i, &n); sl@0: g_print (" %9llu | %9llu | %9llu\n", vals[0], vals[2], vals[1]); sl@0: g_free (vals); sl@0: } sl@0: } sl@0: else sl@0: g_print ("Done.\n"); sl@0: #ifdef __SYMBIAN32__ sl@0: testResultXml("slice-test"); sl@0: #endif /* EMULATOR */ sl@0: return 0; sl@0: } sl@0: }