On 11/3/20 3:43 PM, Patrick Palka wrote:
Profiling revealed that sat_hasher::equal accounts for nearly 40% of
compile time in some cmcstl2 tests.

This patch eliminates this bottleneck by caching the ATOMIC_CONSTRs
returned by normalize_atom.  This in turn allows us to replace the
expensive atomic_constraints_identical_p check in sat_hasher::equal
with cheap pointer equality, with no loss in cache hit rate.

With this patch, compile time for the cmcstl2 test
test/algorithm/set_symmetric_difference4.cpp drops from 19s to 11s with
an --enable-checking=release compiler.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?

gcc/cp/ChangeLog:

        * constraint.cc (struct atom_hasher): New descriptor class for a
        hash_table.  Use it to define ...
        (atom_cache): ... this.
        (normalize_atom): Use it to cache ATOMIC_CONSTRs when not
        generating diagnostics.
        (sat_hasher::hash): Use htab_hash_pointer instead of
        hash_atomic_constraint.
        (sat_hasher::equal): Test for pointer equality instead of
        atomic_constraints_identical_p.
---
  gcc/cp/constraint.cc | 37 ++++++++++++++++++++++++++++++++++---
  1 file changed, 34 insertions(+), 3 deletions(-)

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index b6f6f0d02a5..ce720c641e8 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -710,6 +710,25 @@ normalize_concept_check (tree check, tree args, norm_info 
info)
    return normalize_expression (def, subst, info);
  }
+/* Hash functions for ATOMIC_CONSTRs. */
+
+struct atom_hasher : default_hash_traits<tree>
+{
+  static hashval_t hash (tree atom)
+  {
+    return hash_atomic_constraint (atom);
+  }
+
+  static bool equal (tree atom1, tree atom2)
+  {
+    return atomic_constraints_identical_p (atom1, atom2);
+  }
+};

This is the same as constraint_hash in logic.cc; either they should be combined, or (probably) the hash table in logic.cc should be changed to also take advantage of pointer equivalence.

+/* Used by normalize_atom to cache ATOMIC_CONSTRs.  */
+
+static GTY((deletable)) hash_table<atom_hasher> *atom_cache;

If we're relying on pointer identity, this can't be deletable; if GC discards it, later normalization will generate a new equivalent ATOMIC_CONSTR, breaking the uniqueness assumption.

  /* The normal form of an atom depends on the expression. The normal
     form of a function call to a function concept is a check constraint
     for that concept. The normal form of a reference to a variable
@@ -729,7 +748,19 @@ normalize_atom (tree t, tree args, norm_info info)
    /* Build a new info object for the atom.  */
    tree ci = build_tree_list (t, info.context);
- return build1 (ATOMIC_CONSTR, ci, map);
+  tree atom = build1 (ATOMIC_CONSTR, ci, map);
+  if (!info.generate_diagnostics ())
+    {
+      /* Cache the ATOMIC_CONSTRs that we return, so that sat_hasher::equal
+        later can quickly compare two atoms using just pointer equality.  */
+      if (!atom_cache)
+       atom_cache = hash_table<atom_hasher>::create_ggc (31);
+      tree *slot = atom_cache->find_slot (atom, INSERT);
+      if (*slot)
+       return *slot;
+      *slot = atom;
+    }
+  return atom;
  }
/* Returns the normal form of an expression. */
@@ -2284,13 +2315,13 @@ struct sat_hasher : ggc_ptr_hash<sat_entry>
  {
    static hashval_t hash (sat_entry *e)
    {

We could use a comment here about why we can just hash the pointer.

-    hashval_t value = hash_atomic_constraint (e->constr);
+    hashval_t value = htab_hash_pointer (e->constr);
      return iterative_hash_template_arg (e->args, value);
    }
static bool equal (sat_entry *e1, sat_entry *e2)
    {
-    if (!atomic_constraints_identical_p (e1->constr, e2->constr))
+    if (e1->constr != e2->constr)
        return false;
      return template_args_equal (e1->args, e2->args);
    }


Reply via email to