<div dir="ltr">On 7 October 2015 at 04:00, Kenneth Graunke <span dir="ltr"><<a href="mailto:kenneth@whitecape.org" target="_blank">kenneth@whitecape.org</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Sunday, September 20, 2015 07:54:56 PM Rhys Kidd wrote:<br>
> ---<br>
>  src/glsl/opt_constant_propagation.cpp | 63 +++++++++++++++++++++--------------<br>
>  1 file changed, 38 insertions(+), 25 deletions(-)<br>
<br>
</span>Hi Rhys!<br>
<br>
Thanks for looking into this :)  A couple of comments...<br>
<br>
Now that acp_entry isn't used in any lists, you can make it stop<br>
inheriting from exec_node, which will remove the prev/next pointers.<br>
<br>
Do you have any data on whether this speeds up compile times at all?<br>
I was just running 'time glslparsertest' (or 'time shader_runner') on<br>
the shader from [ <a href="https://bugs.freedesktop.org/show_bug.cgi?id=91857" rel="noreferrer" target="_blank">https://bugs.freedesktop.org/show_bug.cgi?id=91857</a> ].<br>
It should be pretty easy to measure (earlier patches shaved whole<br>
seconds off the compile time); it'd be nice to include that in the<br>
commit message.<br>
<div><div class="h5"><br>
> diff --git a/src/glsl/opt_constant_propagation.cpp b/src/glsl/opt_constant_propagation.cpp<br>
> index 184aaa1..b6cbf61 100644<br>
> --- a/src/glsl/opt_constant_propagation.cpp<br>
> +++ b/src/glsl/opt_constant_propagation.cpp<br>
> @@ -95,7 +95,8 @@ public:<br>
>        progress = false;<br>
>        killed_all = false;<br>
>        mem_ctx = ralloc_context(0);<br>
> -      this->acp = new(mem_ctx) exec_list;<br>
> +      this->acp = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,<br>
> +                                          _mesa_key_pointer_equal);<br>
>        this->kills = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,<br>
>                                              _mesa_key_pointer_equal);<br>
>     }<br>
> @@ -118,8 +119,8 @@ public:<br>
>     void handle_if_block(exec_list *instructions);<br>
>     void handle_rvalue(ir_rvalue **rvalue);<br>
><br>
> -   /** List of acp_entry: The available constants to propagate */<br>
> -   exec_list *acp;<br>
> +   /** Hash table of acp_entry: The available constants to propagate */<br>
> +   hash_table *acp;<br>
><br>
>     /**<br>
>      * List of kill_entry: The masks of variables whose values were<br>
> @@ -206,11 +207,13 @@ ir_constant_propagation_visitor::constant_propagation(ir_rvalue **rvalue) {<br>
>        channel = i;<br>
>        }<br>
><br>
> -      foreach_in_list(acp_entry, entry, this->acp) {<br>
> -      if (entry->var == deref->var && entry->write_mask & (1 << channel)) {<br>
> +      hash_entry *acp_hash_entry;<br>
> +      hash_table_foreach(this->acp, acp_hash_entry) {<br>
> +         acp_entry *entry = (acp_entry *) acp_hash_entry->data;<br>
> +         if (entry->var == deref->var && entry->write_mask & (1 << channel)) {<br>
>           found = entry;<br>
>           break;<br>
> -      }<br>
> +         }<br>
<br>
</div></div>This seems a bit strange...here, we're still walking over the entire<br>
hash table, manually checking if each entry's var matches.  In fact, I<br>
don't see any calls to _mesa_hash_table_search in this patch.<br>
<br>
The point of using a hash table is that it provides constant-time / O(1)<br>
lookups of data, compared to a linked list's linear-time / O(n) lookups.<br>
Generally, hash tables have a bit more overhead than a linked list...so<br>
if we're not gaining constant time lookup, I'm not sure it's worth it.<br>
<br>
Perhaps we need to include the write mask in the key somehow?  Or, hash<br>
on variable, but instead of a single entry, get a list of entries back?<br>
<div><div class="h5"><br>
>        }<br>
><br>
>        if (!found)<br>
> @@ -264,11 +267,12 @@ ir_constant_propagation_visitor::visit_enter(ir_function_signature *ir)<br>
>      * block.  Any instructions at global scope will be shuffled into<br>
>      * main() at link time, so they're irrelevant to us.<br>
>      */<br>
> -   exec_list *orig_acp = this->acp;<br>
> +   hash_table *orig_acp = this->acp;<br>
>     hash_table *orig_kills = this->kills;<br>
>     bool orig_killed_all = this->killed_all;<br>
><br>
> -   this->acp = new(mem_ctx) exec_list;<br>
> +   this->acp = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,<br>
> +                                       _mesa_key_pointer_equal);<br>
>     this->kills = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,<br>
>                                           _mesa_key_pointer_equal);<br>
>     this->killed_all = false;<br>
> @@ -345,7 +349,8 @@ ir_constant_propagation_visitor::visit_enter(ir_call *ir)<br>
>     /* Since we're unlinked, we don't (necssarily) know the side effects of<br>
>      * this call.  So kill all copies.<br>
>      */<br>
> -   acp->make_empty();<br>
> +   acp = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,<br>
> +                                 _mesa_key_pointer_equal);<br>
>     this->killed_all = true;<br>
><br>
>     return visit_continue_with_parent;<br>
> @@ -354,24 +359,29 @@ ir_constant_propagation_visitor::visit_enter(ir_call *ir)<br>
>  void<br>
>  ir_constant_propagation_visitor::handle_if_block(exec_list *instructions)<br>
>  {<br>
> -   exec_list *orig_acp = this->acp;<br>
> +   hash_table *orig_acp = this->acp;<br>
>     hash_table *orig_kills = this->kills;<br>
>     bool orig_killed_all = this->killed_all;<br>
><br>
> -   this->acp = new(mem_ctx) exec_list;<br>
> +   this->acp = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,<br>
> +                                       _mesa_key_pointer_equal);<br>
>     this->kills = _mesa_hash_table_create(mem_ctx, _mesa_hash_pointer,<br>
>                                           _mesa_key_pointer_equal);<br>
>     this->killed_all = false;<br>
><br>
>     /* Populate the initial acp with a constant of the original */<br>
> -   foreach_in_list(acp_entry, a, orig_acp) {<br>
> -      this->acp->push_tail(new(this->mem_ctx) acp_entry(a));<br>
> +   hash_entry *htk;<br>
> +   hash_table_foreach(orig_acp, htk) {<br>
> +      acp_entry *a = (acp_entry *) htk->data;<br>
> +      _mesa_hash_table_insert(this->acp, a,<br>
> +                              new(this->mem_ctx) acp_entry(a));<br>
<br>
</div></div>You might consider doing:<br>
<br>
   _mesa_hash_table_insert_pre_hashed(this->acp, htk->hash, a,<br>
                                      new(mem_ctx) acp_entry(a));<br>
<br>
This avoids having to re-hash every entry in the table when creating a<br>
new hash table.  Since both tables use the same hash function, and the<br>
same key, you may as well use the value you already have.<br>
<br>
I do wonder if it would make sense to add a _mesa_hash_table_copy()<br>
function that memcpy's the contents...since that would be even cheaper<br>
still...but here you're actually inserting a new copy of acp_entry, so<br>
I guess that wouldn't work.<br>
<br>
We might be able to get away with not copying the ACP entry...one reason<br>
for doing so was because it contained prev/next pointers, and so could<br>
only be present in one linked list at a time.  Now that we're using hash<br>
tables, we don't have that restriction.  However, it looks like kill()<br>
actually modifies ACP entries, so it's probably easier to continue<br>
copying them for now.<br>
</blockquote></div><br></div><div class="gmail_extra">Hello Kenneth,<br><br></div><div class="gmail_extra">Thank you for your feedback. I'll review and then resubmit a v2 patchset addressing these comments.<br><br></div><div class="gmail_extra">Regards,<br></div><div class="gmail_extra">Rhys<br></div></div>