Reduce memory waste due to non-power-of-2 allocs
authorTom Tromey <tromey@redhat.com>
Mon, 17 Oct 2011 10:00:07 +0000 (10:00 +0000)
committerDodji Seketeli <dodji@gcc.gnu.org>
Mon, 17 Oct 2011 10:00:07 +0000 (12:00 +0200)
This patch basically arranges for the allocation size of line_map
buffers to be as close as possible to a power of two.  This
*significantly* decreases peak memory consumption as (macro) maps are
numerous and stay live during all the compilation.

The patch adds a new ggc_round_alloc_size interface to the ggc
allocator.  In each of the two main allocator implementations ('page'
and 'zone') the function has been extracted from the main allocation
function code and returns the actual size of the allocated memory
region, thus giving a chance to the caller to maximize the amount of
memory it actually uses from the allocated memory region.  In the
'none' allocator implementation (that uses xmalloc) the
ggc_round_alloc_size just returns the requested allocation size.

Co-Authored-By: Dodji Seketeli <dodji@redhat.com>
From-SVN: r180086

gcc/ChangeLog
gcc/ggc-none.c
gcc/ggc-page.c
gcc/ggc-zone.c
gcc/ggc.h
gcc/toplev.c
libcpp/ChangeLog
libcpp/include/line-map.h
libcpp/line-map.c

index 81caf547177c71d2a162d46c1e3cf7433163972b..557916c82bcfbc6104f68e092f885a14602babd6 100644 (file)
@@ -1,3 +1,19 @@
+2011-10-15  Tom Tromey  <tromey@redhat.com>
+           Dodji Seketeli  <dodji@redhat.com>
+
+       * ggc.h (ggc_round_alloc_size): Declare new public entry point.
+       * ggc-none.c (ggc_round_alloc_size): New public stub function.
+       * ggc-page.c (ggc_alloced_size_order_for_request): New static
+       function.  Factorized from ggc_internal_alloc_stat.
+       (ggc_round_alloc_size): New public function.  Uses
+       ggc_alloced_size_order_for_request.
+       (ggc_internal_alloc_stat): Use ggc_alloced_size_order_for_request.
+       * ggc-zone.c (ggc_round_alloc_size): New public function extracted
+       from ggc_internal_alloc_zone_stat.
+       (ggc_internal_alloc_zone_stat): Use ggc_round_alloc_size.
+       * toplev.c (general_init): Initialize
+       line_table->alloced_size_for_request.
+
 2011-10-15  Tom Tromey  <tromey@redhat.com>
            Dodji Seketeli  <dodji@redhat.com>
 
index 97d25b9b6536c48bab0d00ef4ad1534689b2885a..e57d61787dfd5ee99c22777451d66715e3e765fb 100644 (file)
@@ -39,6 +39,15 @@ ggc_alloc_typed_stat (enum gt_types_enum ARG_UNUSED (gte), size_t size
   return xmalloc (size);
 }
 
+/* For a given size of memory requested for allocation, return the
+   actual size that is going to be allocated.  */
+
+size_t
+ggc_round_alloc_size (size_t requested_size)
+{
+  return requested_size;
+}
+
 void *
 ggc_internal_alloc_stat (size_t size MEM_STAT_DECL)
 {
index a218f7696d7aaba28dc3a9d6425118cc37f2e851..beee851dd7f133022452c3750be0295f3792be29 100644 (file)
@@ -1054,6 +1054,47 @@ static unsigned char size_lookup[NUM_SIZE_LOOKUP] =
   9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
 };
 
+/* For a given size of memory requested for allocation, return the
+   actual size that is going to be allocated, as well as the size
+   order.  */
+
+static void
+ggc_round_alloc_size_1 (size_t requested_size,
+                       size_t *size_order,
+                       size_t *alloced_size)
+{
+  size_t order, object_size;
+
+  if (requested_size < NUM_SIZE_LOOKUP)
+    {
+      order = size_lookup[requested_size];
+      object_size = OBJECT_SIZE (order);
+    }
+  else
+    {
+      order = 10;
+      while (requested_size > (object_size = OBJECT_SIZE (order)))
+        order++;
+    }
+
+  if (size_order)
+    *size_order = order;
+  if (alloced_size)
+    *alloced_size = object_size;
+}
+
+/* For a given size of memory requested for allocation, return the
+   actual size that is going to be allocated.  */
+
+size_t
+ggc_round_alloc_size (size_t requested_size)
+{
+  size_t size = 0;
+  
+  ggc_round_alloc_size_1 (requested_size, NULL, &size);
+  return size;
+}
+
 /* Typed allocation function.  Does nothing special in this collector.  */
 
 void *
@@ -1072,17 +1113,7 @@ ggc_internal_alloc_stat (size_t size MEM_STAT_DECL)
   struct page_entry *entry;
   void *result;
 
-  if (size < NUM_SIZE_LOOKUP)
-    {
-      order = size_lookup[size];
-      object_size = OBJECT_SIZE (order);
-    }
-  else
-    {
-      order = 10;
-      while (size > (object_size = OBJECT_SIZE (order)))
-       order++;
-    }
+  ggc_round_alloc_size_1 (size, &order, &object_size);
 
   /* If there are non-full pages for this size allocation, they are at
      the head of the list.  */
index d0c1d79f53d9ba256904ff118cced85b0b1ed7de..79c8c03296674df0b9937143c4400bd16a1a9b79 100644 (file)
@@ -1073,6 +1073,24 @@ free_chunk (char *ptr, size_t size, struct alloc_zone *zone)
     fprintf (G.debug_file, "Deallocating object, chunk=%p\n", (void *)chunk);
 }
 
+/* For a given size of memory requested for allocation, return the
+   actual size that is going to be allocated.  */
+
+size_t
+ggc_round_alloc_size (size_t requested_size)
+{
+  size_t size;
+
+  /* Make sure that zero-sized allocations get a unique and freeable
+     pointer.  */
+  if (requested_size == 0)
+    size = MAX_ALIGNMENT;
+  else
+    size = (requested_size + MAX_ALIGNMENT - 1) & -MAX_ALIGNMENT;
+
+  return size;
+}
+
 /* Allocate a chunk of memory of at least ORIG_SIZE bytes, in ZONE.  */
 
 void *
@@ -1084,14 +1102,7 @@ ggc_internal_alloc_zone_stat (size_t orig_size, struct alloc_zone *zone
   struct small_page_entry *entry;
   struct alloc_chunk *chunk, **pp;
   void *result;
-  size_t size = orig_size;
-
-  /* Make sure that zero-sized allocations get a unique and freeable
-     pointer.  */
-  if (size == 0)
-    size = MAX_ALIGNMENT;
-  else
-    size = (size + MAX_ALIGNMENT - 1) & -MAX_ALIGNMENT;
+  size_t size = ggc_alloced_size_for_request (orig_size);
 
   /* Try to allocate the object from several different sources.  Each
      of these cases is responsible for setting RESULT and SIZE to
index 30eca66c3023af49a771ba1158b2b12732f0750e..704237cc045d448661f2ed811f0a7f49b5dfe9b5 100644 (file)
--- a/gcc/ggc.h
+++ b/gcc/ggc.h
@@ -145,6 +145,8 @@ extern void gt_pch_save (FILE *f);
 /* The internal primitive.  */
 extern void *ggc_internal_alloc_stat (size_t MEM_STAT_DECL);
 
+extern size_t ggc_round_alloc_size (size_t requested_size);
+
 #define ggc_internal_alloc(s) ggc_internal_alloc_stat (s MEM_STAT_INFO)
 
 /* Allocate an object of the specified type and size.  */
index f508196b3ebcbe4ee1c99655377fbd92fa7be43e..86eed5d63a81904e885380b545090081a65db5f7 100644 (file)
@@ -1216,6 +1216,7 @@ general_init (const char *argv0)
   line_table = ggc_alloc_line_maps ();
   linemap_init (line_table);
   line_table->reallocator = realloc_for_line_map;
+  line_table->round_alloc_size = ggc_round_alloc_size;
   init_ttree ();
 
   /* Initialize register usage now so switches may override.  */
index fad0da6585318594bc149bcda3f9936c63a1585e..375072003affde1cfe46936c4e93b7e375666a78 100644 (file)
@@ -1,3 +1,11 @@
+2011-10-15  Tom Tromey  <tromey@redhat.com>
+           Dodji Seketeli  <dodji@redhat.com>
+
+       * include/line-map.h (struct line_maps::alloced_size_for_request):
+       New member.
+       * line-map.c (new_linemap): Use set->alloced_size_for_request to
+       get the actual allocated size of line maps.
+
 2011-10-15  Tom Tromey  <tromey@redhat.com>
            Dodji Seketeli  <dodji@redhat.com>
 
index 572e330a1bd070b0206e6e8f9a57f49a0004bbbf..1e2a148e17bcf86a074a044cdbad8b0fbc0b490a 100644 (file)
@@ -53,6 +53,10 @@ typedef unsigned int source_location;
 /* Memory allocation function typedef.  Works like xrealloc.  */
 typedef void *(*line_map_realloc) (void *, size_t);
 
+/* Memory allocator function that returns the actual allocated size,
+   for a given requested allocation.  */
+typedef size_t (*line_map_round_alloc_size_func) (size_t);
+
 /* An ordinary line map encodes physical source locations. Those
    physical source locations are called "spelling locations".
    
@@ -281,6 +285,10 @@ struct GTY(()) line_maps {
   /* If non-null, the allocator to use when resizing 'maps'.  If null,
      xrealloc is used.  */
   line_map_realloc reallocator;
+
+  /* The allocators' function used to know the actual size it
+     allocated, for a certain allocation size requested.  */
+  line_map_round_alloc_size_func round_alloc_size;
 };
 
 /* Returns the pointer to the memory region where information about
index 9086b3e30651f3d2ebca1b40ae1b7d3417552c55..87b8bfe8a0f63e742e3687d5500bf268383f2e70 100644 (file)
@@ -92,16 +92,43 @@ new_linemap (struct line_maps *set,
   if (LINEMAPS_USED (set, macro_map_p) == LINEMAPS_ALLOCATED (set, macro_map_p))
     {
       /* We ran out of allocated line maps. Let's allocate more.  */
+      unsigned alloc_size;
 
       line_map_realloc reallocator
        = set->reallocator ? set->reallocator : xrealloc;
+      line_map_round_alloc_size_func round_alloc_size =
+       set->round_alloc_size;
+
+      /* We are going to execute some dance to try to reduce the
+        overhead of the memory allocator, in case we are using the
+        ggc-page.c one.
+        
+        The actual size of memory we are going to get back from the
+        allocator is the smallest power of 2 that is greater than the
+        size we requested.  So let's consider that size then.  */
+
+      alloc_size =
+       (2 * LINEMAPS_ALLOCATED (set, macro_map_p) +  256)
+       * sizeof (struct line_map);
+
+      /* Get the actual size of memory that is going to be allocated
+        by the allocator.  */
+      alloc_size = round_alloc_size (alloc_size);
+
+      /* Now alloc_size contains the exact memory size we would get if
+        we have asked for the initial alloc_size amount of memory.
+        Let's get back to the number of macro map that amounts
+        to.  */
       LINEMAPS_ALLOCATED (set, macro_map_p) =
-       2 * LINEMAPS_ALLOCATED (set, macro_map_p) + 256;
-      LINEMAPS_MAPS (set, macro_map_p)
-       = (struct line_map *) (*reallocator) (LINEMAPS_MAPS (set, macro_map_p),
-                                             LINEMAPS_ALLOCATED (set,
-                                                                 macro_map_p)
-                                             * sizeof (struct line_map));
+       alloc_size / (sizeof (struct line_map));
+
+      /* And now let's really do the re-allocation.  */
+      LINEMAPS_MAPS (set, macro_map_p) =
+       (struct line_map *) (*reallocator)
+       (LINEMAPS_MAPS (set, macro_map_p),
+        (LINEMAPS_ALLOCATED (set, macro_map_p)
+         * sizeof (struct line_map)));
+
       result =
        &LINEMAPS_MAPS (set, macro_map_p)[LINEMAPS_USED (set, macro_map_p)];
       memset (result, 0,