Index: glib/gmem.c
===================================================================
RCS file: /cvs/gnome/glib/glib/gmem.c,v
retrieving revision 1.47
diff -u -r1.47 gmem.c
--- glib/gmem.c	22 Mar 2005 04:09:16 -0000	1.47
+++ glib/gmem.c	26 Sep 2005 04:17:05 -0000
@@ -127,6 +127,7 @@
 
 
 /* --- functions --- */
+
 gpointer
 g_malloc (gulong n_bytes)
 {
@@ -602,8 +603,9 @@
 
 #endif	/* !G_DISABLE_CHECKS */
 
-
+#if 0
 /* --- MemChunks --- */
+
 typedef struct _GFreeAtom      GFreeAtom;
 typedef struct _GMemArea       GMemArea;
 
@@ -878,7 +880,7 @@
   mem_chunk->mem_area->free -= mem_chunk->atom_size;
   mem_chunk->mem_area->allocated += 1;
 
-outa_here:
+ outa_here:
 
   LEAVE_MEM_CHUNK_ROUTINE ();
 
@@ -1059,8 +1061,9 @@
     }
 
   g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
-	 "%s: %ld bytes using %d mem areas",
-	 mem_chunk->name, mem, mem_chunk->num_mem_areas);
+	 "%s: chunk sized %d, %ld bytes using %d mem areas, allocated %d bytes",
+	 mem_chunk->name, mem_chunk->atom_size, mem, mem_chunk->num_mem_areas,
+	 mem_chunk->num_mem_areas * mem_chunk->area_size);
 }
 
 void
@@ -1217,6 +1220,497 @@
 void	g_blow_chunks		(void)			{}
 
 #endif /* DISABLE_MEM_POOLS */
+#endif
+
+/* fast allocator */
+#define AREA_SIZE 1024
+#define NUM_DEPOTS 16
+
+typedef struct _Head Head;
+typedef struct _Block Block;
+typedef struct _Magazine Magazine;
+typedef struct _Depot Depot;
+
+struct _Block 
+{
+  Block *next2;
+  Block *next;
+};
+
+struct _Magazine
+{
+  Block *block[2];
+  gint   count[2];
+};
+
+struct _Depot
+{
+  Block *depot;
+  Block *rest;
+  gint rest_count;
+
+  guint chunk_size;
+  guint magazine_size;
+
+  guint n_areas;
+  
+  GStaticMutex lock;
+};
+
+static Depot depots[NUM_DEPOTS];
+static gint min_block_size;
+
+static inline void
+initialize_depots (void)
+{
+  gint i;
+  gint chunk_size;
+  static gboolean initialized = FALSE;
+
+  if (G_UNLIKELY (!initialized))
+    {
+      initialized = TRUE;
+
+      //g_atexit (g_mem_chunk_info);
+
+      min_block_size = ((sizeof (Block) + G_MEM_ALIGN - 1) / G_MEM_ALIGN) * G_MEM_ALIGN; 
+
+      chunk_size = min_block_size;
+      for (i = 0; i < NUM_DEPOTS; i++)
+	{
+	  depots[i].depot = NULL;
+	  depots[i].rest = NULL;
+	  depots[i].rest_count = 0;
+	  depots[i].chunk_size = chunk_size;
+	  depots[i].magazine_size = AREA_SIZE / chunk_size;
+	  depots[i].n_areas = 0;
+
+	  g_static_mutex_init (&depots[i].lock);
+	  
+	  chunk_size += G_MEM_ALIGN;
+	}
+    }
+}
+
+static inline void
+unload_partial (gint   idx,
+                Block *block)
+{
+  Depot *depot;
+  Block *next;
+  Block *head;
+
+  if (!block)
+    return;
+
+  depot = &depots[idx];
+
+  g_static_mutex_lock (&depot->lock);
+
+  while (block)
+    {
+      next = block->next2;
+      block->next2 = depot->rest;
+      depot->rest = block;
+      depot->rest_count++;
+      block = next;
+      
+      if (depot->rest_count == depot->magazine_size)
+	{
+	  head = depot->rest;
+          if (head->next)
+           {
+             head->next->next2 = head->next2;
+             head->next2 = head->next;
+           }
+
+          head->next = depot->depot;
+	  depot->depot = head;
+	  depot->rest = NULL;
+	  depot->rest_count = 0;   
+	}
+    } 
+
+  g_static_mutex_unlock (&depot->lock);   
+}
+
+void
+_g_mem_thread_cleanup (void)
+{ 
+  gint idx;
+  Magazine *mag;
+
+  for (idx = 0; idx < NUM_DEPOTS; idx++)
+    {
+      mag = _g_thread_mem_private_get (g_thread_self (), idx);
+      if (mag)
+	{
+	  unload_partial (idx, mag->block[0]);
+	  unload_partial (idx, mag->block[1]);
+
+	  g_free (mag);
+
+	  _g_thread_mem_private_set (g_thread_self (), idx, NULL);
+	}
+    }
+}
+
+static inline gint
+get_index (gint chunk_size)
+{
+  return (MAX (chunk_size - min_block_size, 0) + G_MEM_ALIGN - 1) / G_MEM_ALIGN;
+}
+
+static inline Magazine *
+get_magazine (gint idx)
+{
+  GThread *self;
+  Magazine *mag;
+
+  self = g_thread_self ();
+
+  mag = (Magazine *)_g_thread_mem_private_get (self, idx);
+
+  if (G_UNLIKELY (!mag))
+    {
+      mag = g_new0 (Magazine, 1);
+      
+      _g_thread_mem_private_set (self, idx, mag);
+    }
+
+  return mag;
+}
+
+
+static inline Block *
+reload (gint idx)
+{
+  gchar *mem;
+  Depot *depot;
+  Block *block, *next;
+  gint i;
+
+  initialize_depots ();
+  
+  depot = &depots[idx];
+
+  g_static_mutex_lock (&depot->lock);
+  
+  if (G_UNLIKELY (!depot->depot))
+    {
+      mem = g_malloc (AREA_SIZE);
+      depot->n_areas++;
+      block = next = NULL;
+      for (i = 0; i < depot->magazine_size; i++)
+	{
+	  block = (Block *) (mem + i * depot->chunk_size);
+          block->next = NULL;
+	  block->next2 = next;
+	  next = block;
+	}
+
+      depot->depot = block;
+    }
+
+  block = depot->depot;
+  depot->depot = block->next;
+  block->next = NULL;
+ 
+  g_static_mutex_unlock (&depot->lock);
+
+  return block;
+}
+
+static inline void
+unload (gint   idx,
+	Block *block)
+{
+  Depot *depot;
+
+  depot = &depots[idx];
+
+  g_static_mutex_lock (&depot->lock);
+
+  if (block->next)
+   {
+     block->next->next2 = block->next2;
+     block->next2 = block->next;
+   }
+
+  block->next = depot->depot;
+  depot->depot = block;
+ 
+  g_static_mutex_unlock (&depot->lock);
+}
+
+static inline void
+swap_magazine (Magazine *magazine)
+{
+  Block *block;
+  gint count;
+
+  block = magazine->block[0];
+  count = magazine->count[0];
+  magazine->block[0] = magazine->block[1];
+  magazine->count[0] = magazine->count[1];
+  magazine->block[1] = block;
+  magazine->count[1] = count;
+}
+
+gpointer
+g_alloc_chunk (gint chunk_size)
+{
+  gint idx;
+  Magazine *magazine;
+  Block *block;
+  
+  idx = get_index (chunk_size);
+
+  if (G_UNLIKELY (idx >= NUM_DEPOTS))
+    return g_malloc (chunk_size);
+
+  magazine = get_magazine (idx);
+
+  if (G_UNLIKELY (!magazine->block[0]))
+    {
+      if (G_UNLIKELY (!magazine->block[1]))
+	{
+	  magazine->block[0] = reload (idx);
+	  magazine->count[0] = depots[idx].magazine_size;
+	}
+      else
+	{
+          swap_magazine (magazine);
+	}
+    }
+
+  block = magazine->block[0];
+  if (block->next)
+    {
+      block->next->next2 = block->next2;
+      magazine->block[0] = block->next;
+    }
+  else
+    {
+      magazine->block[0] = block->next2;
+      magazine->count[0]--;
+    }
+  
+  return block;
+}
+
+gpointer
+g_alloc0_chunk (gint chunk_size)
+{
+  gpointer mem;
+
+  mem = g_alloc_chunk (chunk_size);
+
+  if (mem)
+    memset (mem, 0, chunk_size);
+
+  return mem;
+}
+
+
+void
+g_free_chunk_list (gpointer mem,
+                   gint     chunk_size)
+{
+  gint idx;
+  Magazine *magazine;
+  Block *block;
+  
+  if (!mem)
+    return;
+ 
+  idx = get_index (chunk_size);
+  if (G_UNLIKELY (idx >= NUM_DEPOTS))
+    {
+      g_free (mem);
+      return;
+    }
+
+  magazine = get_magazine (idx);
+
+  if (G_UNLIKELY (magazine->count[1] == depots[idx].magazine_size))
+    {
+      if (G_UNLIKELY (magazine->count[0] == depots[idx].magazine_size))
+	{
+	  unload (idx, magazine->block[1]);
+	  magazine->block[1] = NULL;
+	  magazine->count[1] = 0;
+	}
+      else
+	{
+          swap_magazine (magazine);
+	}
+    }
+
+  block = (Block *)mem;
+  block->next2 = magazine->block[1];
+  magazine->block[1] = block;
+  magazine->count[1]++;
+}
+
+void
+g_free_chunk (gpointer mem,
+              gint     chunk_size)
+{
+  Block *block;
+
+  if (!mem)
+    return;
+
+  block = (Block *)mem;
+  block->next = NULL;
+
+  g_free_chunk_list (mem, chunk_size);
+}
+
+static void
+collect_stats (gpointer data, 
+	       gpointer user_data)
+{
+  GThread *thread = (GThread *)data;
+  gint *stats = (gint *)user_data;
+  Magazine *mag;
+  gint i;
+
+  for (i = 0; i < NUM_DEPOTS; i++)
+    {
+      mag = (Magazine *)_g_thread_mem_private_get (thread, i);
+      
+      if (mag)
+	{
+	  stats[i] += mag->count[0];
+	  stats[i] += mag->count[1];
+	}
+    }
+
+  stats[16]++;
+}
+
+/* TODO 
+ * compare used/free numbers for threaded apps and unthreaded apps
+ * allocate and free tons of lists
+ * make cut-off size configurable
+ */
+static void 
+print_depot_info (Depot *depot)
+{
+  Block *head; 
+  gint free_chunks;
+  gint stats[NUM_DEPOTS + 1] = { 0, };
+  gint idx;
+  
+  free_chunks = depot->rest_count;
+  for (head = depot->depot; head; head = head->next2)
+    free_chunks += depot->magazine_size;
+
+  _g_thread_foreach (collect_stats, stats);
+
+  idx = get_index (depot->chunk_size);
+
+  g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
+	 "Depot (%d / %d): mem areas %d (%d bytes), global free %d (%d bytes), local free %d (%d bytes) in %d threads",
+	 depot->chunk_size, 
+	 depot->magazine_size,
+	 depot->n_areas,
+	 AREA_SIZE * depot->n_areas, 
+	 free_chunks,
+	 free_chunks * depot->chunk_size,
+	 stats[idx],
+	 stats[idx] * depot->chunk_size,
+	 stats[NUM_DEPOTS]);
+}
+
+struct _GMemChunk
+{
+  Depot *depot;
+  gchar *name;
+  guint chunk_size;
+};
+
+GMemChunk *
+g_mem_chunk_new (const gchar *name,
+		 gint         atom_size, 
+		 gulong       area_size,
+		 gint         type)
+{
+  GMemChunk *mem_chunk;
+
+  mem_chunk = g_malloc (sizeof (GMemChunk));
+
+  mem_chunk->name = g_strdup (name);
+  atom_size = ((atom_size + G_MEM_ALIGN - 1) / G_MEM_ALIGN) * G_MEM_ALIGN;
+  if (atom_size < sizeof (Block))
+    atom_size = sizeof (Block);
+
+  mem_chunk->chunk_size = atom_size;
+
+  if (mem_chunk->chunk_size > sizeof (Block) + 15 * G_MEM_ALIGN)
+    mem_chunk->depot = NULL;
+  else
+    {
+      initialize_depots ();
+      
+      mem_chunk->depot = &depots[(mem_chunk->chunk_size - sizeof (Block)) / G_MEM_ALIGN];
+    }
+
+  return mem_chunk;
+}
+
+void
+g_mem_chunk_destroy (GMemChunk *mem_chunk)
+{
+  g_free (mem_chunk->name);
+  g_free (mem_chunk);
+}
+
+gpointer
+g_mem_chunk_alloc (GMemChunk *mem_chunk)
+{
+  return g_alloc_chunk (mem_chunk->chunk_size);
+}
+
+gpointer
+g_mem_chunk_alloc0 (GMemChunk *mem_chunk)
+{
+  return g_alloc0_chunk (mem_chunk->chunk_size);
+}
+
+void
+g_mem_chunk_free (GMemChunk *mem_chunk,
+                  gpointer   mem)
+{
+  g_free_chunk (mem, mem_chunk->chunk_size);
+}
+
+void	g_mem_chunk_clean	(GMemChunk *mem_chunk)	{}
+void	g_mem_chunk_reset	(GMemChunk *mem_chunk)	{}
+
+void
+g_mem_chunk_print (GMemChunk *mem_chunk)
+{
+  if (mem_chunk->depot)
+    g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
+	   "%s: using depot %d", mem_chunk->name, mem_chunk->chunk_size);
+  else
+    g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
+	   "%s: using malloc", mem_chunk->name);
+}
+
+void
+g_mem_chunk_info (void)			
+{
+  gint i;
+  
+  for (i = 0; i < 16; i++)
+    print_depot_info (&depots[i]);
+}
+ 
+void g_blow_chunks (void) {}
 
 
 /* generic allocators
@@ -1268,12 +1762,15 @@
 void
 _g_mem_thread_init (void)
 {
+#if 0
 #ifndef DISABLE_MEM_POOLS
   mem_chunks_lock = g_mutex_new ();
 #endif
+#endif
 #ifndef G_DISABLE_CHECKS
   g_profile_mutex = g_mutex_new ();
 #endif
+  initialize_depots ();
 }
 
 void
Index: glib/gmem.h
===================================================================
RCS file: /cvs/gnome/glib/glib/gmem.h,v
retrieving revision 1.12
diff -u -r1.12 gmem.h
--- glib/gmem.h	22 Mar 2005 04:09:16 -0000	1.12
+++ glib/gmem.h	26 Sep 2005 04:17:05 -0000
@@ -55,6 +55,13 @@
 gpointer g_try_realloc    (gpointer	 mem,
 			   gulong	 n_bytes);
 
+gpointer g_alloc_chunk     (gint     chunk_size);
+gpointer g_alloc0_chunk    (gint     chunk_size);
+void     g_free_chunk      (gpointer mem,
+	                    gint     chunk_size);
+void     g_free_chunk_list (gpointer mem,
+	                    gint     chunk_size);
+
 
 /* Convenience memory allocators
  */
