<div dir="ltr"><div class="gmail_default" style="font-family:verdana,sans-serif">Thank you Stepan for your comments.</div><div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">
While I agree that a better hash would be more expensive to compute, it won't be a factor as long as it is linear on the size of the function, even if you have to walk the whole routine. After all, the comparison may need to walk over the whole routine as well (and do so n log(n) times, instead of just n times).</div>
<div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">Where the comparison approach has an edge is that it can bail out on to the first difference, while the hashing needs to be fully computed all the time. This may be a noticeable diference in practice. That is why a multiple-hash approach may be interesting to explore. Use a very fast hash first, and then compute a more expensive hash on the second level as we go down into each bucket. Even multiple levels where we slowly look further down into the function may be appropriate -- eg look at the first block on the first hash, then the next three blocks on the second hash, etc...</div>
<div class="gmail_default" style="font-family:verdana,sans-serif"><br></div><div class="gmail_default" style="font-family:verdana,sans-serif">Also, some heuristics related to code size may be relevant here -- as the size of the function increases the chances of finding a match likely go down very quickly.</div>
</div><div class="gmail_extra"><br><br><div class="gmail_quote">On Wed, Jan 22, 2014 at 7:35 AM, Stepan Dyatkovskiy <span dir="ltr"><<a href="mailto:stpworld@narod.ru" target="_blank">stpworld@narod.ru</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi Raul and Duncan,<br>
<br>
Duncan,<br>
Thank you for review. I hope to present fixed patch tomorrow.<br>
<br>
First, I would like to show few performance results:<br>
<br>
command: "time opt -mergefunc <test>"<br>
<br>
File: tramp3d-v4.ll, 12963 functions<br>
                  Current : Patched<br>
Time:              9.8 s  : 2.0 secs  ; >400%!!<br>
Functions merged:  3503   : 3507<br>
<br>
File: spirit.ll, 2885 functions<br>
                  Current : Patched<br>
Time:              2.2 s  : 0.5 s<br>
Functions merged:  1503   : 1505<br>
<br>
File: k.ll, 2788 functions<br>
                  Current : Patched<br>
Time:              1.5 s  : 0.7 s<br>
Functions merged:  1626   : 1626<br>
<br>
File: sqlite3.ll, 1057 functions<br>
                  Current : Patched<br>
Time:              0.4 s  : 0.4 s<br>
Functions merged:  7      : 7<br>
<br>
All the tests were token from test-suite object directory. In average it shows >200% performance improvement. You could also see that patched version detects a bit more functions for merging, since it has improved constant-equivalence detection.<br>

<br>
Raul,<br>
<br>
Yes. you right it is O(N*log(N)). And yes, I have some ideas about hashing.<br>
Though in the end of hashing improvement I still see the tree. I'll explain.<br>
<br>
Once you calculated hash (even really good one), you still have probability of N>1 functions with the same hash. Of course it could be really low. But then, you have to spend some time just being creating such a complex hash (definitely, you have to scan whole function body).<br>

<br>
I think there should be sequence of short hash numbers. In another words we can try to represent function as a chain of hash codes:<br>
F0: hashF0-0, hashF0-1, hashF0-2, ...<br>
F1: hashF1-0, hashF1-1, ...<br>
<br>
In this case you can create kind of hash-tree. Consider we have F0, F1 and F2.<br>
<br>
Imagine, hashF0-0 == hashF1-0 == hashF2-0, hashF0-1 == hashF1-1:<br>
<br>
Then we can build the next tree (requires fixed-width fonts):<br>
<br>
             [hashF0-0]<br>
              /      \<br>
     [hashF0-1]      [hashF2-1]<br>
      /      \             \<br>
[hashF0-2]  [hashF1-2]   [hashF2-2]<br>
<br>
In this case as you can see, whole process would be of complexity O(N*n), where:<br>
<br>
N - number of functions<br>
n - function size.<br>
<br>
Note, hashF[i]-[j] could be generated on demand. It is not obvious to generate whole hash at once.<br>
<br>
I have also implemented this approach. Though it is looks more complex, and not so fast, as I hoped. Perhaps I can make it simplier. But it could not be simpler that just to replace "bool" with -1,0,1. Actually I really wanted to propose idea with hash-trees, since it combines advantages of hashing and tree-based lookup routines. But then I discovered idea I presented finally, and saw that it shows almost same improvement and looks much simpler.<span class="HOEnZb"><font color="#888888"><br>

<br>
-Stepan</font></span><div class="HOEnZb"><div class="h5"><br>
<br>
Duncan P. N. Exon Smith wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Hi Stepan,<br>
<br>
This looks interesting!  Some high-level comments:<br>
<br>
- Please post the patch untarred next time.  Also, I'm not sure if it's typical<br>
   to provide supporting documents in .doc format; while many of us probably<br>
   have access to Word, a more portable and email-friendly format (like text,<br>
   markdown or rst) is probably better.<br>
- Have you profiled this?  What's the speedup?  I second Raul's question about<br>
   whether improving the hashing (or doing secondary hashing) could result in a<br>
   larger benefit (maybe even with simpler code).<br>
- At the beginning of FunctionComparator::<u></u>cmpEnumerate, you have a comment that<br>
   you can't introduce an order relation for cross-references, and the following<br>
   code:<br>
<br>
         // Unfortunately we can't introduce order relation for<br>
         // cross reference case. It is possible for self-reference only.<br>
         if (V1 == F1) {<br>
           if (V2 == F2)<br>
             return 0;<br>
           return -1;<br>
         }<br>
         if (V2 == F2) {<br>
           if (V1 == F1)<br>
             return 0;<br>
           return 1;<br>
         }<br>
<br>
     Can't this break asymmetry?  Doesn't breaking the ordering relation cause a<br>
     correctness problem?  I.e., what am I missing here?<br>
<br>
I also have a few low-level comments (and nitpicks):<br>
<br>
- The comments at the top of the file don't match the new implementation!<br>
- Lots of strange tabbing after your changes.  E.g.:<br>
<br>
         +  int cmpGEP(const GetElementPtrInst *GEP1,<br>
                                 const GetElementPtrInst *GEP2) {<br>
<br>
- After the FunctionPtr class declaration, you have an extra blank line.  There<br>
   are a few of these scattered through (e.g., at the beginning of<br>
   FunctionComparator::<u></u>cmpConstants).<br>
- Your new helper functions (such as cmpNumbers) that are local to the file<br>
   should be declared 'static'.<br>
- You use the following pattern throughout:<br>
<br>
         int Res;<br>
         // ...<br>
         Res = cmpNumbers(I1->getNumOperands(<u></u>), I2->getNumOperands());<br>
         if (Res != 0)<br>
           return Res;<br>
<br>
     whereas, since you never use Res unless it's non-zero, I think the<br>
     following is more clear and concise:<br>
<br>
         if (int Res = cmpNumbers(I1->getNumOperands(<u></u>), I2->getNumOperands()))<br>
           return Res;<br>
<br>
- I'm not sure if your MERGEFUNC_SANITY should be included the way it is:<br>
<br>
         +//#define MERGEFUNC_SANITY 900<br>
         +#ifdef MERGEFUNC_SANITY<br>
<br>
- I’m also unsure about the following:<br>
<br>
         +  // If we have some problematic functions, perhaps we would like<br>
         +  // to keep declarations only, and those definitions fires the problem.<br>
         +  // We could insert manualy last ones (notepad, nano, vi, whatever else :-) )<br>
         +#if 0<br>
         +  for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {<br>
         +    Function *F = I;<br>
         +      F->deleteBody();<br>
         +  }<br>
         +#endif<br>
<br>
Cheers,<br>
Duncan<br>
<br>
On Jan 21, 2014, at 9:58 AM, Stepan Dyatkovskiy <<a href="mailto:stpworld@narod.ru" target="_blank">stpworld@narod.ru</a>> wrote:<br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
ping<br>
Stepan Dyatkovskiy wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Hi all,<br>
<br>
I propose simple improvement for MergeFunctions pass, that reduced its<br>
complexity from O(N^2) to O(log(N)), where N is number of functions in<br>
module.<br>
<br>
The idea, is to replace the result of comparison from "bool" to<br>
"-1,0,1". In another words: define order relation on functions set.<br>
To be sure, that functions could be comparable that way, we have to<br>
prove that order relation is possible on all comparison stage.<br>
<br>
The last one is possible, if for each comparison stage we implement has<br>
next properties:<br>
* reflexivity (a <= a, a == a, a >= a),<br>
* antisymmetry (if a <= b and b <= a then a  == b),<br>
* transitivity (a <= b and b <= c, then a <= c)<br>
* asymmetry (if a < b, then a > b or a == b).<br>
</blockquote></blockquote>
<br>
For asymmetry, I think it’s: if a < b, then not a > b and not a == b.<br>
<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Once we have defined order relation we can store all the functions in<br>
binary tree and perform lookup in O(log(N)) time.<br>
<br>
This post has two attachments:<br>
1. The patch, that has implementation of this idea.<br>
2. The MergeFunctions pass detailed description, with explanation how<br>
order relation could be possible.<br>
<br>
Hope it helps to make things better!<br>
<br>
-Stepan.<br>
<br>
<br>
______________________________<u></u>_________________<br>
LLVM Developers mailing list<br>
<a href="mailto:LLVMdev@cs.uiuc.edu" target="_blank">LLVMdev@cs.uiuc.edu</a>         <a href="http://llvm.cs.uiuc.edu" target="_blank">http://llvm.cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev" target="_blank">http://lists.cs.uiuc.edu/<u></u>mailman/listinfo/llvmdev</a><br>
<br>
</blockquote>
<br>
______________________________<u></u>_________________<br>
LLVM Developers mailing list<br>
<a href="mailto:LLVMdev@cs.uiuc.edu" target="_blank">LLVMdev@cs.uiuc.edu</a>         <a href="http://llvm.cs.uiuc.edu" target="_blank">http://llvm.cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev" target="_blank">http://lists.cs.uiuc.edu/<u></u>mailman/listinfo/llvmdev</a><br>
</blockquote>
<br>
</blockquote>
<br>
</div></div></blockquote></div><br><br clear="all"><div><br></div>-- <br><div dir="ltr"><div><font size="4" face="arial black, sans-serif" style="background-color:rgb(0,0,0)" color="#b45f06"> Raúl E. Silvera </font></div>
<div><br></div></div>
</div>