implement chain walking for arbitrary ->next pointer offsets in
authorTim Janik <timj@imendio.com>
Mon, 5 Dec 2005 15:01:27 +0000 (15:01 +0000)
committerTim Janik <timj@src.gnome.org>
Mon, 5 Dec 2005 15:01:27 +0000 (15:01 +0000)
Mon Dec  5 15:53:20 2005  Tim Janik  <timj@imendio.com>

        * glib/gslice.c: implement chain walking for arbitrary ->next pointer
        offsets in g_slice_free_chain_with_offset() based on a patch by behdad
        in bug 323178. moved time consuming logic from g_slice_free() out of
        the inner loop, so g_slice_free_chain_with_offset() provides a real
        performance benefit over g_slice_free1() now.

        * glib/gslice.h: renamed g_slice_free_chain() to
        g_slice_free_chain_with_offset(). implemented g_slice_free_chain() as
        a type-safe macro as suggested in bug 323178.
        simplified the macro implementation of g_slice_free() and implemented
        it in a type safe manner for all compliers as suggested by Morten
        Welinder <mortenw@gnome.org>.

        * glib/gmain.c:
        * glib/glist.c:
        * glib/gslist.c:
        * glib/glib.symbols: s/g_slice_free_chain/g_slice_free_chain_with_offset/

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-12
docs/reference/ChangeLog
docs/reference/glib/tmpl/memory_slices.sgml
glib/glib.symbols
glib/glist.c
glib/gmain.c
glib/gslice.c
glib/gslice.h
glib/gslist.c

index 9a22a33..f35cc7f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+Mon Dec  5 15:53:20 2005  Tim Janik  <timj@imendio.com>
+
+       * glib/gslice.c: implement chain walking for arbitrary ->next pointer
+       offsets in g_slice_free_chain_with_offset() based on a patch by behdad
+       in bug 323178. moved time consuming logic from g_slice_free() out of
+       the inner loop, so g_slice_free_chain_with_offset() provides a real
+       performance benefit over g_slice_free1() now.
+       
+       * glib/gslice.h: renamed g_slice_free_chain() to
+       g_slice_free_chain_with_offset(). implemented g_slice_free_chain() as
+       a type-safe macro as suggested in bug 323178.
+       simplified the macro implementation of g_slice_free() and implemented
+       it in a type safe manner for all compliers as suggested by Morten
+       Welinder <mortenw@gnome.org>.
+
+       * glib/gmain.c:
+       * glib/glist.c:
+       * glib/gslist.c:
+       * glib/glib.symbols: s/g_slice_free_chain/g_slice_free_chain_with_offset/
+
 2005-12-05  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/gasyncqueue.c: Add some docs.
index 9a22a33..f35cc7f 100644 (file)
@@ -1,3 +1,23 @@
+Mon Dec  5 15:53:20 2005  Tim Janik  <timj@imendio.com>
+
+       * glib/gslice.c: implement chain walking for arbitrary ->next pointer
+       offsets in g_slice_free_chain_with_offset() based on a patch by behdad
+       in bug 323178. moved time consuming logic from g_slice_free() out of
+       the inner loop, so g_slice_free_chain_with_offset() provides a real
+       performance benefit over g_slice_free1() now.
+       
+       * glib/gslice.h: renamed g_slice_free_chain() to
+       g_slice_free_chain_with_offset(). implemented g_slice_free_chain() as
+       a type-safe macro as suggested in bug 323178.
+       simplified the macro implementation of g_slice_free() and implemented
+       it in a type safe manner for all compliers as suggested by Morten
+       Welinder <mortenw@gnome.org>.
+
+       * glib/gmain.c:
+       * glib/glist.c:
+       * glib/gslist.c:
+       * glib/glib.symbols: s/g_slice_free_chain/g_slice_free_chain_with_offset/
+
 2005-12-05  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/gasyncqueue.c: Add some docs.
index 9a22a33..f35cc7f 100644 (file)
@@ -1,3 +1,23 @@
+Mon Dec  5 15:53:20 2005  Tim Janik  <timj@imendio.com>
+
+       * glib/gslice.c: implement chain walking for arbitrary ->next pointer
+       offsets in g_slice_free_chain_with_offset() based on a patch by behdad
+       in bug 323178. moved time consuming logic from g_slice_free() out of
+       the inner loop, so g_slice_free_chain_with_offset() provides a real
+       performance benefit over g_slice_free1() now.
+       
+       * glib/gslice.h: renamed g_slice_free_chain() to
+       g_slice_free_chain_with_offset(). implemented g_slice_free_chain() as
+       a type-safe macro as suggested in bug 323178.
+       simplified the macro implementation of g_slice_free() and implemented
+       it in a type safe manner for all compliers as suggested by Morten
+       Welinder <mortenw@gnome.org>.
+
+       * glib/gmain.c:
+       * glib/glist.c:
+       * glib/gslist.c:
+       * glib/glib.symbols: s/g_slice_free_chain/g_slice_free_chain_with_offset/
+
 2005-12-05  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/gasyncqueue.c: Add some docs.
index 3fad981..7f3346f 100644 (file)
@@ -1,3 +1,8 @@
+Mon Dec  5 15:53:37 2005  Tim Janik  <timj@imendio.com>
+
+       * glib/tmpl/memory_slices.sgml: updates to new g_slice API
+       and minor fixes.
+
 2005-12-05  Matthias Clasen  <mclasen@redhat.com>
 
        * gobject/tmpl/generic_values.sgml: 
index 790a7d0..643dd4c 100644 (file)
@@ -6,18 +6,21 @@ efficient way to allocate groups of equal-sized chunks of memory.
 
 <!-- ##### SECTION Long_Description ##### -->
 <para>
-Memory slices provide a space-efficient way to allocate equal-sized
-pieces of memory, just like #GMemChunks, while avoiding their scalability
-and performance problems. 
+Memory slices provide a space-efficient and multi processing scalable
+way to allocate equal-sized pieces of memory, just like the original
+#GMemChunks (from GLib <= 2.8), while avoiding their excessive
+memroy-waste scalability and performance problems.
 </para>
 
 <para>
 To achieve these goals, the slice allocator uses a sophisticated, 
 layered design that has been inspired by Bonwick's slab allocator
-<footnote><para><ulink url="http://citeseer.ist.psu.edu/bonwick94slab.html">[Bonwick94]</ulink> Jeff Bonwick, The slab allocator: An object-caching kernel
+<footnote><para>
+<ulink url="http://citeseer.ist.psu.edu/bonwick94slab.html">[Bonwick94]</ulink> Jeff Bonwick, The slab allocator: An object-caching kernel
 memory allocator. USENIX 1994, and  
- <ulink url="http://citeseer.ist.psu.edu/bonwick01magazines.html">[Bonwick01]</ulink> Bonwick and Jonathan Adams, Magazines and vmem: Extending the
-slab allocator to many cpu's and arbitrary resources. USENIX 2001</para></footnote>.
+<ulink url="http://citeseer.ist.psu.edu/bonwick01magazines.html">[Bonwick01]</ulink> Bonwick and Jonathan Adams, Magazines and vmem: Extending the
+slab allocator to many cpu's and arbitrary resources. USENIX 2001
+</para></footnote>.
 It uses posix_memalign() to optimize allocations of many equally 
 sized chunks, and has per-thread free lists (the so-called magazine layer) 
 to quickly satisfy allocation requests of already known structure sizes. 
@@ -94,17 +97,19 @@ object size used at allocation time is still available when freeing.
 <!-- ##### FUNCTION g_slice_alloc ##### -->
 <para>
 Allocates a block of memory from the slice allocator.
+The block adress handed out is guaranteed to be aligned
+to at leats 2 * sizeof (void*).
 </para>
 
 @block_size: the number of bytes to allocate
-@Returns: a pointer to the allocated 
+@Returns: a pointer to the allocated memory block
 @Since: 2.10
 
 
 <!-- ##### FUNCTION g_slice_alloc0 ##### -->
 <para>
-Allocates a block of memory from the slice allocator, setting the
-memory to 0.
+Allocates a block of memory via g_slice_alloc()
+and initialize the returned memory to 0.
 </para>
 
 @block_size: the number of bytes to allocate
@@ -114,38 +119,48 @@ memory to 0.
 
 <!-- ##### FUNCTION g_slice_free1 ##### -->
 <para>
-Frees a block of memory. The memory must have been allocated from
-the slice allocator.
+Frees a block of memory. The memory must have been allocated via
+g_slice_alloc() or g_slice_alloc0()
+and the @block_size has to match the size specified upon allocation.
 </para>
 
 @block_size: the size of the block
 @mem_block: a pointer to the block to free
 @Since: 2.10
 
-<!-- ##### FUNCTION g_slice_free_chain ##### -->
+<!-- ##### FUNCTION g_slice_free_chain_with_offset ##### -->
 <para>
-Frees a linked list of memory block. The memory blocks must be equal-sized, 
-allocated from the slice allocator and linked together by a 
-<literal>next</literal> pointer stored in the @next_offset's word of 
-each block. 
+Frees a linked list of memory blocks. The memory blocks must be equal-sized, 
+allocated via
+g_slice_alloc() or g_slice_alloc0()
+and linked together by a <literal>next</literal> pointer (similar to #GSList)
+stored in the word of each block denoted by @next_offset.
+The @block_size has to match the size specified upon allocation.
 </para>
-<para>
-Currently, this function only supports blocks which store their 
-<literal>next</literal> pointer in the same position as #GSList. 
-Therefore, @next_offset must be 1.
-</para>
-
 @block_size: the size of the blocks
 @mem_chain: a pointer to the first block
 @next_offset: the offset of the <literal>next</literal> pointer
 @Since: 2.10
 
+<!-- ##### MACRO g_slice_free_chain ##### -->
+<para>
+Frees a linked list of memory blocks of structure type @type.
+The memory blocks must be equal-sized, allocated via
+g_slice_alloc() or g_slice_alloc0()
+and linked together by a @next pointer (similar to #GSList). The name of the
+@next field in @type is passed as third argument.
+</para>
+@type:        the type of the @mem_chain blocks
+@mem_chain:   a pointer to the first block of the chain
+@next:        the field name of the next pointer in @type
+@Since: 2.10
+
 
 <!-- ##### MACRO g_slice_new ##### -->
 <para>
 A convenience macro to allocate a block of memory from the slice allocator.
-It calls g_slice_alloc() and casts the returned pointer to a pointer to
-the given type, avoiding a type cast in the source code.
+It calls g_slice_alloc() with sizeof (@type) and casts the returned pointer
+to a pointer of the given type, avoiding a type cast in the source code.
 </para>
 
 @type: the type to allocate, typically a structure name
@@ -156,9 +171,9 @@ the given type, avoiding a type cast in the source code.
 <!-- ##### MACRO g_slice_new0 ##### -->
 <para>
 A convenience macro to allocate a block of memory from the slice allocator
-and set the memory to 0. It calls g_slice_alloc0() and casts the returned 
-pointer to a pointer to the given type, avoiding a type cast in the source 
-code.
+and set the memory to 0. It calls g_slice_alloc0() with sizeof (@type) and
+casts the returned pointer to a pointer of the given type, avoiding a type
+cast in the source code.
 </para>
 
 @type: the type to allocate, typically a structure name
index aec5e49..bfbb560 100644 (file)
@@ -608,7 +608,7 @@ g_blow_chunks
 g_slice_alloc G_GNUC_MALLOC
 g_slice_alloc0 G_GNUC_MALLOC
 g_slice_free1
-g_slice_free_chain
+g_slice_free_chain_with_offset
 g_slice_set_config
 g_slice_get_config
 g_slice_get_config_state
index 1e9a96f..9f756b5 100644 (file)
@@ -49,7 +49,7 @@ g_list_alloc (void)
 void
 g_list_free (GList *list)
 {
-  g_slice_free_chain (sizeof (GList), list, G_STRUCT_OFFSET (GList, next));
+  g_slice_free_chain (GList, list, next);
 }
 
 void
index e3a47b7..0595015 100644 (file)
@@ -189,7 +189,7 @@ struct _GChildWatchSource
 struct _GPollRec
 {
   GPollFD *fd;
-  GPollRec *next; /* chaining via second pointer member allows use of g_slice_free_chain() */
+  GPollRec *next;
   gint priority;
 };
 
@@ -599,7 +599,7 @@ static inline void
 poll_rec_list_free (GMainContext *context,
                    GPollRec     *list)
 {
-  g_slice_free_chain (sizeof (GPollRec), list, G_STRUCT_OFFSET (GPollRec, next));
+  g_slice_free_chain (GPollRec, list, next);
 }
 
 /**
index ddc3ddc..683d2be 100644 (file)
@@ -772,24 +772,16 @@ g_slice_free1 (gsize    mem_size,
       slab_allocator_free_chunk (chunk_size, mem_block);
       g_mutex_unlock (allocator->slab_mutex);
     }
-  else                          /* delegate to system malloc */
+  else                                  /* delegate to system malloc */
     g_free (mem_block);
 }
 
 void
-g_slice_free_chain (gsize    mem_size,
-                    gpointer mem_chain,
-                    gsize    next_offset)
-{
-  GSList *slice = mem_chain;
-  g_return_if_fail (next_offset == G_STRUCT_OFFSET (GSList, next));
-  g_return_if_fail (mem_size >= sizeof (GSList));
-  while (slice)
-    {
-      GSList *current = slice;
-      slice = slice->next;
-      g_slice_free1 (mem_size, current);
-    }
+g_slice_free_chain_with_offset (gsize    mem_size,
+                                gpointer mem_chain,
+                                gsize    next_offset)
+{
+  gpointer slice = mem_chain;
   /* while the thread magazines and the magazine cache are implemented so that
    * they can easily be extended to allow for free lists containing more free
    * lists for the first level nodes, which would allow O(1) freeing in this
@@ -801,7 +793,47 @@ g_slice_free_chain (gsize    mem_size,
    * - memory usage histograms on larger applications seem to indicate that
    *   the amount of released multi node lists is negligible in comparison
    *   to single node releases.
+   * - the major performance bottle neck, namely g_private_get() or
+   *   g_mutex_lock()/g_mutex_unlock() has already been moved out of the
+   *   inner loop for freeing chained slices.
    */
+  gsize chunk_size = P2ALIGN (mem_size);
+  guint acat = allocator_categorize (chunk_size);
+  if (G_LIKELY (acat == 1))             /* allocate through magazine layer */
+    {
+      ThreadMemory *tmem = thread_memory_from_self();
+      guint ix = SLAB_INDEX (allocator, chunk_size);
+      while (slice)
+        {
+          guint8 *current = slice;
+          slice = *(gpointer*) (current + next_offset);
+          if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
+            {
+              thread_memory_swap_magazines (tmem, ix);
+              if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
+                thread_memory_magazine2_unload (tmem, ix);
+            }
+          thread_memory_magazine2_free (tmem, ix, current);
+        }
+    }
+  else if (acat == 2)                   /* allocate through slab allocator */
+    {
+      g_mutex_lock (allocator->slab_mutex);
+      while (slice)
+        {
+          guint8 *current = slice;
+          slice = *(gpointer*) (current + next_offset);
+          slab_allocator_free_chunk (chunk_size, current);
+        }
+      g_mutex_unlock (allocator->slab_mutex);
+    }
+  else                                  /* delegate to system malloc */
+    while (slice)
+      {
+        guint8 *current = slice;
+        slice = *(gpointer*) (current + next_offset);
+        g_free (current);
+      }
 }
 
 /* --- single page allocator --- */
index 4a9cbda..a320148 100644 (file)
@@ -29,28 +29,35 @@ G_BEGIN_DECLS
 
 /* slices - fast allocation/release of small memory blocks
  */
-gpointer g_slice_alloc          (gsize   block_size) G_GNUC_MALLOC;
-gpointer g_slice_alloc0         (gsize    block_size) G_GNUC_MALLOC;
-void     g_slice_free1          (gsize    block_size,
-                                gpointer mem_block);
-void     g_slice_free_chain     (gsize    block_size,
-                                gpointer mem_chain,
-                                gsize    next_offset);
+gpointer g_slice_alloc                 (gsize    block_size) G_GNUC_MALLOC;
+gpointer g_slice_alloc0                (gsize    block_size) G_GNUC_MALLOC;
+void     g_slice_free1                 (gsize    block_size,
+                                        gpointer mem_block);
+void     g_slice_free_chain_with_offset (gsize    block_size,
+                                        gpointer mem_chain,
+                                        gsize    next_offset);
 #define  g_slice_new(type)      ((type*) g_slice_alloc (sizeof (type)))
 #define  g_slice_new0(type)     ((type*) g_slice_alloc0 (sizeof (type)))
-/*       g_slice_free(type,mem) g_slice_free1 (sizeof (type), mem) */
+/*       g_slice_free                   (MemoryBlockType,
+ *                                      MemoryBlockType *mem_block);
+ *       g_slice_free_chain             (MemoryBlockType,
+ *                                       MemoryBlockType *first_chain_block,
+ *                                       memory_block_next_field);
+ * pseudo prototypes for the macro
+ * definitions following below.
+ */
+
+/* we go through extra hoops to ensure type safety */
+#define g_slice_free(type, mem)                                do {    \
+  if (1) g_slice_free1 (sizeof (type), (mem));                 \
+  else   (void) ((type*) 0 == (mem));                          \
+} while (0)
+#define g_slice_free_chain(type, mem_chain, next)      do {    \
+  if (1) g_slice_free_chain_with_offset (sizeof (type),                \
+                 (mem_chain), G_STRUCT_OFFSET (type, next));   \
+  else   (void) ((type*) 0 == (mem_chain));                    \
+} while (0)
 
-#if    __GNUC__ >= 2
-/* for GCC, define a type-safe variant of g_slice_free() */
-#define g_slice_free(type, mem)        ({                       \
-  void (*g_slice_free) (gsize, type*);                          \
-  while (0) g_slice_free (sizeof (type), mem);                  \
-  g_slice_free1 (sizeof (type), mem);                           \
-})
-#else  /* !__GNUC__ */
-#define        g_slice_free(type, mem) g_slice_free1 (sizeof (type) + (gsize) (type*) 0, mem)
-/* we go through the extra (gsize)(type*)0 hoop to ensure a known type argument */
-#endif /* !__GNUC__ */
 
 /* --- internal debugging API --- */
 typedef enum {
index 6483f07..ebf0348 100644 (file)
@@ -49,7 +49,7 @@ g_slist_alloc (void)
 void
 g_slist_free (GSList *slist)
 {
-  g_slice_free_chain (sizeof (GSList), slist, G_STRUCT_OFFSET (GSList, next));
+  g_slice_free_chain (GSList, slist, next);
 }
 
 void