PCH objects are currently laid out in saving_htab order, which is the
hash of their address, so objects that are used together (such as the
trees of one function body) end up scattered over the file. The PCH is
mapped privately, so every page a translation unit writes to gets
copied, and with a large C++ PCH most pages end up dirty even for a
trivial translation unit.
This patch allocates the objects in the order in which
gt_pch_note_object first sees them, which roughly follows a depth-first
walk from the roots, so related objects end up next to each other within
the region of their size order. It also adds
gt_pch_note_likely_modified to put objects that are likely to be written
to at the start of their region, which c_common_write_pch uses for the
declarations of functions with a body.
The generated code and the size of the PCH are unchanged. Peak private
memory and wall time of cc1plus with a 10 MB preprocessed C++ header,
at --param=ggc-min-expand=10:
empty TU 92 functions
before 476 MB 12.5 s 728 MB 32.7 s
after 356 MB 7.8 s 606 MB 25.9 s
Assisted-by: Claude Opus 5.5
gcc/ChangeLog:
* ggc.h (gt_pch_note_likely_modified): Declare.
* ggc-common.cc: Include sbitmap.h.
(struct ptr_data): Add seq field.
(pch_note_seq, pch_likely_modified): New variables.
(gt_pch_note_object): Set seq.
(gt_pch_note_likely_modified): New function.
(ggc_call_alloc): Rename to...
(ggc_call_collect): ...this. Store the object at index seq of
state->ptrs instead of allocating space for it.
(gt_pch_save): Reset pch_note_seq. Record the sequence numbers
of the objects passed to gt_pch_note_likely_modified in a bitmap.
Allocate space for the objects in the order in which they were
noted, those in the bitmap first.
gcc/c-family/ChangeLog:
* c-pch.cc: Include cgraph.h.
(c_common_write_pch): Call gt_pch_note_likely_modified for the
declarations of the functions with a body.
Signed-off-by: Chris Burr <[email protected]>
---
gcc/c-family/c-pch.cc | 8 ++++++
gcc/ggc-common.cc | 61 ++++++++++++++++++++++++++++++++++++++-----
gcc/ggc.h | 3 +++
3 files changed, 66 insertions(+), 6 deletions(-)
diff --git a/gcc/c-family/c-pch.cc b/gcc/c-family/c-pch.cc
index 8976edfe851..ee18acaee04 100644
--- a/gcc/c-family/c-pch.cc
+++ b/gcc/c-family/c-pch.cc
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "target.h"
#include "c-common.h"
+#include "cgraph.h"
#include "timevar.h"
#include "flags.h"
#include "debug.h"
@@ -173,6 +174,13 @@ c_common_write_pch (void)
cpp_write_pch_deps (parse_in, pch_outfile);
+ /* The compilations using the PCH may compile the functions defined in
+ it, modifying their declarations. */
+ cgraph_node *node;
+ FOR_EACH_FUNCTION (node)
+ if (DECL_SAVED_TREE (node->decl) || DECL_STRUCT_FUNCTION (node->decl))
+ gt_pch_note_likely_modified (node->decl);
+
gt_pch_save (pch_outfile);
timevar_push (TV_PCH_CPP_SAVE);
diff --git a/gcc/ggc-common.cc b/gcc/ggc-common.cc
index 8752fad81b7..381450dffd7 100644
--- a/gcc/ggc-common.cc
+++ b/gcc/ggc-common.cc
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see
#include "hosthooks.h"
#include "plugin.h"
#include "options.h"
+#include "sbitmap.h"
/* When true, protect the contents of the identifier hash table. */
bool ggc_protect_identifiers = true;
@@ -262,8 +263,16 @@ struct ptr_data
gt_handle_reorder reorder_fn;
size_t size;
void *new_addr;
+ /* The number of objects noted before this one. */
+ size_t seq;
};
+/* The number of objects noted so far by gt_pch_note_object. */
+static size_t pch_note_seq;
+
+/* The objects passed to gt_pch_note_likely_modified. */
+static vec<void *> pch_likely_modified;
+
#define POINTER_HASH(x) (hashval_t)((intptr_t)x >> 3)
/* Helper for hashing saving_htab. */
@@ -314,6 +323,7 @@ gt_pch_note_object (void *obj, void *note_ptr_cookie,
*slot = XCNEW (struct ptr_data);
(*slot)->obj = obj;
+ (*slot)->seq = pch_note_seq++;
(*slot)->note_ptr_fn = note_ptr_fn;
(*slot)->note_ptr_cookie = note_ptr_cookie;
if (length_override != (size_t)-1)
@@ -363,6 +373,16 @@ gt_pch_note_reorder (void *obj, void *note_ptr_cookie,
data->reorder_fn = reorder_fn;
}
+/* Note that compilations using the PCH are likely to modify OBJ, so that
+ gt_pch_save puts it together with the other such objects. This must be
+ called before gt_pch_save. */
+
+void
+gt_pch_note_likely_modified (void *obj)
+{
+ pch_likely_modified.safe_push (obj);
+}
+
/* Handy state for the traversal functions. */
struct traversal_state
@@ -386,13 +406,16 @@ ggc_call_count (ptr_data **slot, traversal_state *state)
return 1;
}
+/* Store the object in *SLOT in STATE->ptrs at the index given by the order
+ in which it was noted. */
+
int
-ggc_call_alloc (ptr_data **slot, traversal_state *state)
+ggc_call_collect (ptr_data **slot, traversal_state *state)
{
struct ptr_data *d = *slot;
- d->new_addr = ggc_pch_alloc_object (state->d, d->obj, d->size);
- state->ptrs[state->ptrs_i++] = d;
+ gcc_checking_assert (d->seq < state->count);
+ state->ptrs[d->seq] = d;
return 1;
}
@@ -553,12 +576,22 @@ gt_pch_save (FILE *f)
timevar_push (TV_PCH_PTR_REALLOC);
saving_htab = new hash_table<saving_hasher> (50000);
+ pch_note_seq = 0;
for (rt = gt_ggc_rtab; *rt; rt++)
for (rti = *rt; rti->base != NULL; rti++)
for (i = 0; i < rti->nelt; i++)
(*rti->pchw)(*(void **)((char *)rti->base + rti->stride * i));
+ /* The objects passed to gt_pch_note_likely_modified that are saved, by
+ their sequence number. */
+ auto_sbitmap likely_modified (pch_note_seq);
+ bitmap_clear (likely_modified);
+ for (void *obj : pch_likely_modified)
+ if (ptr_data *d = saving_htab->find_with_hash (obj, POINTER_HASH (obj)))
+ bitmap_set_bit (likely_modified, d->seq);
+ pch_likely_modified.release ();
+
/* Prepare the objects for writing, determine addresses and such. */
state.f = f;
state.d = init_ggc_pch ();
@@ -580,10 +613,26 @@ gt_pch_save (FILE *f)
ggc_pch_this_base (state.d, mmi.preferred_base);
+ /* Allocate space for the objects in the order in which they were noted,
+ rather than in the order of saving_htab, which depends on their
+ addresses. That is roughly the order of a depth-first walk from the
+ roots, except that the elements of chains are noted together, so
+ objects that refer to each other, such as the trees of a function body,
+ end up close to each other, at least those of the same size order:
+ ggc_pch_alloc_object puts objects of different size orders in
+ different regions. Allocate the objects that compilations using the
+ PCH are likely to modify first, so that they share as few pages as
+ possible with the others. Those compilations then touch, and modify
+ (and so copy), fewer pages of the PCH. */
+ gcc_assert (state.count == pch_note_seq);
state.ptrs = XNEWVEC (struct ptr_data *, state.count);
- state.ptrs_i = 0;
-
- saving_htab->traverse <traversal_state *, ggc_call_alloc> (&state);
+ saving_htab->traverse <traversal_state *, ggc_call_collect> (&state);
+ for (int pass = 0; pass < 2; pass++)
+ for (i = 0; i < state.count; i++)
+ if (bitmap_bit_p (likely_modified, i) == (pass == 0))
+ state.ptrs[i]->new_addr = ggc_pch_alloc_object (state.d,
+ state.ptrs[i]->obj,
+ state.ptrs[i]->size);
timevar_pop (TV_PCH_PTR_REALLOC);
timevar_push (TV_PCH_PTR_SORT);
diff --git a/gcc/ggc.h b/gcc/ggc.h
index fe9850e191d..716fa182131 100644
--- a/gcc/ggc.h
+++ b/gcc/ggc.h
@@ -55,6 +55,9 @@ extern void gt_pch_note_callback (void *, void *);
function. */
extern void gt_pch_note_reorder (void *, void *, gt_handle_reorder);
+/* Note that compilations using the PCH are likely to modify an object. */
+extern void gt_pch_note_likely_modified (void *);
+
/* generated function to clear caches in gc memory. */
extern void gt_clear_caches ();
--
2.47.1