<div dir="ltr"><div class="gmail_quote"><div dir="ltr">On Tue, Nov 20, 2018 at 6:40 PM Ian Romanick <<a href="mailto:idr@freedesktop.org">idr@freedesktop.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">From: Ian Romanick <<a href="mailto:ian.d.romanick@intel.com" target="_blank">ian.d.romanick@intel.com</a>><br>
<br>
Changes in peak memory usage according to Valgrind massif:<br>
<br>
mean soft fp64 using uint64:   5,499,881,802 => 1,343,998,123<br>
gfxbench5 aztec ruins high 11:    62,415,414 =>    62,415,414<br>
deus ex mankind divided 148:      62,317,965 =>    62,317,965<br>
deus ex mankind divided 2890:     72,585,466 =>    72,585,466<br>
dirt showdown 676:                74,473,151 =>    75,203,703<br>
dolphin ubershaders 210:         109,637,096 =>    83,185,248<br>
<br>
Signed-off-by: Ian Romanick <<a href="mailto:ian.d.romanick@intel.com" target="_blank">ian.d.romanick@intel.com</a>><br>
---<br>
 src/compiler/nir/nir_phi_builder.c | 115 ++++++++++++++++++++++++++++++-------<br>
 1 file changed, 93 insertions(+), 22 deletions(-)<br>
<br>
diff --git a/src/compiler/nir/nir_phi_builder.c b/src/compiler/nir/nir_phi_builder.c<br>
index add3efd2dfc..36ab9888f48 100644<br>
--- a/src/compiler/nir/nir_phi_builder.c<br>
+++ b/src/compiler/nir/nir_phi_builder.c<br>
@@ -41,6 +41,25 @@ struct nir_phi_builder {<br>
    unsigned iter_count;<br>
    unsigned *work;<br>
    nir_block **W;<br>
+<br>
+   /**<br>
+    * Tables of SSA defs, indexed by block and nir_phi_builder_value.<br>
+    *<br>
+    * For each [value, block] tuple, this table has one of three types of<br>
+    * values:<br>
+    *<br>
+    *  - NULL (i.e., not in the table). Indicates that there is no known<br>
+    *    definition in this block.  If you need to find one, look at the<br>
+    *    block's immediate dominator.<br>
+    *<br>
+    *  - NEEDS_PHI. Indicates that the block may need a phi node but none has<br>
+    *    been created yet.  If a def is requested for a block, a phi will need<br>
+    *    to be created.<br>
+    *<br>
+    *  - A regular SSA def.  This will be either the result of a phi node or<br>
+    *    one of the defs provided by nir_phi_builder_value_set_blocK_def().<br>
+    */<br>
+   struct hash_table *value_block_def_hash;<br>
 };<br>
<br>
 #define NEEDS_PHI ((nir_ssa_def *)(intptr_t)-1)<br>
@@ -61,23 +80,34 @@ struct nir_phi_builder_value {<br>
     * blocks.<br>
     */<br>
    struct exec_list phis;<br>
+};<br>
<br>
-   /* Array of SSA defs, indexed by block.  For each block, this array has has<br>
-    * one of three types of values:<br>
-    *<br>
-    *  - NULL. Indicates that there is no known definition in this block.  If<br>
-    *    you need to find one, look at the block's immediate dominator.<br>
-    *<br>
-    *  - NEEDS_PHI. Indicates that the block may need a phi node but none has<br>
-    *    been created yet.  If a def is requested for a block, a phi will need<br>
-    *    to be created.<br>
-    *<br>
-    *  - A regular SSA def.  This will be either the result of a phi node or<br>
-    *    one of the defs provided by nir_phi_builder_value_set_blocK_def().<br>
-    */<br>
-   nir_ssa_def *defs[0];<br>
+/**<br>
+ * Key for the nir_phi_builder::value_block_def_hash.<br>
+ */<br>
+struct phi_builder_key {<br>
+   struct nir_phi_builder_value *val;<br>
+   unsigned block_index;<br>
 };<br>
<br>
+static uint32_t<br>
+phi_builder_key_hash(const void *_k)<br>
+{<br>
+   const struct phi_builder_key *k = (const struct phi_builder_key *)_k;<br>
+<br>
+   return _mesa_fnv32_1a_accumulate(_mesa_fnv32_1a_accumulate(0, k->val),<br>
+                                    k->block_index);<br>
+}<br>
+<br>
+static bool<br>
+phi_builder_key_equals(const void *_a, const void *_b)<br>
+{<br>
+   const struct phi_builder_key *a = (const struct phi_builder_key *)_a;<br>
+   const struct phi_builder_key *b = (const struct phi_builder_key *)_b;<br>
+<br>
+   return a->val == b->val && a->block_index == b->block_index;<br>
+}<br></blockquote><div><br></div><div>Would it be better to have a hash table per value?  A whole hash table sounds like a lot of overhead but it may be less than a tiny allocation (plus a ralloc header!) for every (block, value) pair.  I guess it depends on the number of defs we expect to be registered per-value.</div><div><br></div><div>Running some on-the-fly numbers... A ralloc header is 6 pointers so a ralloc'd phi_builder_key is 8 pointers worth of data and a ralloc'd hash_table struct is 15 pointers worth.  Throwing in the actual hash table with 7 entries and you have a header and two pointers per entry so that's another 20 pointers for a total of 35.  That's the same as four phi_builder_keys (without including malloc's own overhead).  So if you have at least four blocks involved (there have to be at least 3 or there is no phi), it's break-even and if there are more involved, one table per value is a win.</div><div><br></div><div>We could also consider doing some optimizations for the case where the value is defined in only one block but that gets tricky as everything has an implicit undef at the top so that's probably best left for another time unless it's a super-common case in the fp64 code.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
 struct nir_phi_builder *<br>
 nir_phi_builder_create(nir_function_impl *impl)<br>
 {<br>
@@ -100,6 +130,9 @@ nir_phi_builder_create(nir_function_impl *impl)<br>
    pb->iter_count = 0;<br>
    pb->work = rzalloc_array(pb, unsigned, pb->num_blocks);<br>
    pb->W = ralloc_array(pb, nir_block *, pb->num_blocks);<br>
+   pb->value_block_def_hash = _mesa_hash_table_create(pb,<br>
+                                                      phi_builder_key_hash,<br>
+                                                      phi_builder_key_equals);<br>
<br>
    return pb;<br>
 }<br>
@@ -111,7 +144,7 @@ nir_phi_builder_add_value(struct nir_phi_builder *pb, unsigned num_components,<br>
    struct nir_phi_builder_value *val;<br>
    unsigned i, w_start = 0, w_end = 0;<br>
<br>
-   val = rzalloc_size(pb, sizeof(*val) + sizeof(val->defs[0]) * pb->num_blocks);<br>
+   val = rzalloc_size(pb, sizeof(*val));<br>
    val->builder = pb;<br>
    val->num_components = num_components;<br>
    val->bit_size = bit_size;<br>
@@ -142,7 +175,9 @@ nir_phi_builder_add_value(struct nir_phi_builder *pb, unsigned num_components,<br>
          if (next == pb->impl->end_block)<br>
             continue;<br>
<br>
-         if (val->defs[next->index] == NULL) {<br>
+         const struct phi_builder_key k = { val, next->index };<br>
+<br>
+         if (_mesa_hash_table_search(pb->value_block_def_hash, &k) == NULL) {<br>
             /* Instead of creating a phi node immediately, we simply set the<br>
              * value to the magic value NEEDS_PHI.  Later, we create phi nodes<br>
              * on demand in nir_phi_builder_value_get_block_def().<br>
@@ -164,7 +199,24 @@ void<br>
 nir_phi_builder_value_set_block_def(struct nir_phi_builder_value *val,<br>
                                     nir_block *block, nir_ssa_def *def)<br>
 {<br>
-   val->defs[block->index] = def;<br>
+   struct hash_table *ht = val->builder->value_block_def_hash;<br>
+   const struct phi_builder_key k = { val, block->index };<br>
+   const uint32_t h = ht->key_hash_function(&k);<br>
+<br>
+   /* This doesn't just use _mesa_hash_table_insert because we only want to<br>
+    * allocate memory for the key if we know that the value needs to be added<br>
+    * to the hash table.<br>
+    */<br>
+   struct hash_entry *const he = _mesa_hash_table_search_pre_hashed(ht, h, &k);<br>
+   if (he != NULL) {<br>
+      he->data = def;<br>
+   } else {<br>
+      struct phi_builder_key *kp = ralloc(val->builder, struct phi_builder_key);<br>
+<br>
+      kp->val = k.val;<br>
+      kp->block_index = k.block_index;<br>
+      _mesa_hash_table_insert_pre_hashed(ht, h, kp, def);<br>
+   }<br>
 }<br>
<br>
 nir_ssa_def *<br>
@@ -175,8 +227,20 @@ nir_phi_builder_value_get_block_def(struct nir_phi_builder_value *val,<br>
     * have a valid ssa_def, if any.<br>
     */<br>
    nir_block *dom = block;<br>
-   while (dom && val->defs[dom->index] == NULL)<br>
+   struct hash_entry *he = NULL;<br>
+<br>
+   while (dom != NULL) {<br>
+      const struct phi_builder_key k = { val, dom->index };<br>
+<br>
+      he = _mesa_hash_table_search(val->builder->value_block_def_hash, &k);<br>
+      if (he != NULL)<br>
+         break;<br>
+<br>
       dom = dom->imm_dom;<br>
+   }<br>
+<br>
+   /* Exactly one of (he != NULL) and (dom == NULL) must be true. */<br>
+   assert((he != NULL) != (dom == NULL));<br>
<br>
    nir_ssa_def *def;<br>
    if (dom == NULL) {<br>
@@ -191,7 +255,7 @@ nir_phi_builder_value_get_block_def(struct nir_phi_builder_value *val,<br>
       nir_instr_insert(nir_before_cf_list(&val->builder->impl->body),<br>
                        &undef->instr);<br>
       def = &undef->def;<br>
-   } else if (val->defs[dom->index] == NEEDS_PHI) {<br>
+   } else if (he->data == NEEDS_PHI) {<br>
       /* The magic value NEEDS_PHI indicates that the block needs a phi node<br>
        * but none has been created.  We need to create one now so we can<br>
        * return it to the caller.<br>
@@ -215,13 +279,14 @@ nir_phi_builder_value_get_block_def(struct nir_phi_builder_value *val,<br>
                         val->bit_size, NULL);<br>
       phi->instr.block = dom;<br>
       exec_list_push_tail(&val->phis, &phi->instr.node);<br>
-      def = val->defs[dom->index] = &phi->dest.ssa;<br>
+      def = &phi->dest.ssa;<br>
+      he->data = def;<br>
    } else {<br>
       /* In this case, we have an actual SSA def.  It's either the result of a<br>
        * phi node created by the case above or one passed to us through<br>
        * nir_phi_builder_value_set_block_def().<br>
        */<br>
-      def = val->defs[dom->index];<br>
+      def = (struct nir_ssa_def *) he->data;<br>
    }<br>
<br>
    /* Walk the chain and stash the def in all of the applicable blocks.  We do<br>
@@ -231,8 +296,14 @@ nir_phi_builder_value_get_block_def(struct nir_phi_builder_value *val,<br>
     *     block that is not dominated by this one.<br>
     *  2) To avoid unneeded recreation of phi nodes and undefs.<br>
     */<br>
-   for (dom = block; dom && val->defs[dom->index] == NULL; dom = dom->imm_dom)<br>
+   for (dom = block; dom != NULL; dom = dom->imm_dom) {<br>
+      const struct phi_builder_key k = { val, dom->index };<br>
+<br>
+      if (_mesa_hash_table_search(val->builder->value_block_def_hash, &k) != NULL)<br>
+         break;<br>
+<br>
       nir_phi_builder_value_set_block_def(val, dom, def);<br>
+   }<br>
<br>
    return def;<br>
 }<br>
-- <br>
2.14.4<br>
<br>
_______________________________________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org" target="_blank">mesa-dev@lists.freedesktop.org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev" rel="noreferrer" target="_blank">https://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</blockquote></div></div>