c++: Reuse identical ATOMIC_CONSTRs during normalization
authorPatrick Palka <ppalka@redhat.com>
Mon, 9 Nov 2020 23:12:47 +0000 (18:12 -0500)
committerPatrick Palka <ppalka@redhat.com>
Mon, 9 Nov 2020 23:12:47 +0000 (18:12 -0500)
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.

gcc/cp/ChangeLog:

* constraint.cc (atom_cache): Define this deletable hash_table.
(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.
* cp-tree.h (struct atom_hasher): Moved and renamed from ...
* logic.cc (struct constraint_hash): ... here.
(clause::m_set): Adjust accordingly.

gcc/cp/constraint.cc
gcc/cp/cp-tree.h
gcc/cp/logic.cc

index c871a8a..613ced2 100644 (file)
@@ -710,6 +710,10 @@ normalize_concept_check (tree check, tree args, norm_info info)
   return normalize_expression (def, subst, info);
 }
 
+/* Used by normalize_atom to cache ATOMIC_CONSTRs.  */
+
+static GTY((deletable)) hash_table<atom_hasher> *atom_cache;
+
 /* 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 +733,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 cheaply 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. */
@@ -2294,13 +2310,18 @@ struct sat_hasher : ggc_ptr_hash<sat_entry>
 {
   static hashval_t hash (sat_entry *e)
   {
-    hashval_t value = hash_atomic_constraint (e->constr);
+    /* Since normalize_atom caches the ATOMIC_CONSTRs it returns,
+       we can assume pointer-based identity for fast hashing and
+       comparison.  Even if this assumption is violated, that's
+       okay, we'll just get a cache miss.  */
+    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))
+    /* As in sat_hasher::hash.  */
+    if (e1->constr != e2->constr)
       return false;
     return template_args_equal (e1->args, e2->args);
   }
index 0813730..c7e25df 100644 (file)
@@ -7839,6 +7839,21 @@ extern hashval_t iterative_hash_constraint      (tree, hashval_t);
 extern hashval_t hash_atomic_constraint         (tree);
 extern void diagnose_constraints                (location_t, tree, tree);
 
+/* A structural hasher for ATOMIC_CONSTRs.  */
+
+struct atom_hasher : default_hash_traits<tree>
+{
+  static hashval_t hash (tree t)
+  {
+    return hash_atomic_constraint (t);
+  }
+
+  static bool equal (tree t1, tree t2)
+  {
+    return atomic_constraints_identical_p (t1, t2);
+  }
+};
+
 /* in logic.cc */
 extern bool subsumes                            (tree, tree);
 
index 194b743..6701488 100644 (file)
@@ -47,21 +47,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "toplev.h"
 #include "type-utils.h"
 
-/* Hash functions for atomic constrains.  */
-
-struct constraint_hash : default_hash_traits<tree>
-{
-  static hashval_t hash (tree t)
-  {
-    return hash_atomic_constraint (t);
-  }
-
-  static bool equal (tree t1, tree t2)
-  {
-    return atomic_constraints_identical_p (t1, t2);
-  }
-};
-
 /* A conjunctive or disjunctive clause.
 
    Each clause maintains an iterator that refers to the current
@@ -219,7 +204,7 @@ struct clause
   }
 
   std::list<tree> m_terms; /* The list of terms.  */
-  hash_set<tree, false, constraint_hash> m_set; /* The set of atomic constraints.  */
+  hash_set<tree, false, atom_hasher> m_set; /* The set of atomic constraints.  */
   iterator m_current; /* The current term.  */
 };