<div dir="auto"><div><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, 16 Jun 2020, 11:44 Richard Smith, <<a href="mailto:richard@metafoo.co.uk">richard@metafoo.co.uk</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, 15 Jun 2020, 23:31 Ninu-Ciprian Marginean via cfe-dev, <<a href="mailto:cfe-dev@lists.llvm.org" target="_blank" rel="noreferrer">cfe-dev@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>Hello,</div><div><br></div><div>I want to investigate if there are any possibilities of optimizing virtual functions calls. From my knowledge and reading I understand that the overhead for this is two pointer dereferences. I know about alternatives like CRTP and std::variant, but for this investigation, I'm interested only in traditional, dynamic polymorphism. One of my ideas is to use some form of caching of the computation of the address of the actual method that gets called. Obviously we cannot always do this, but there are some cases where we could.</div><div><br></div><div>One example:</div><div><br></div><div>I have a virtual function call in a loop. All the time, the same method is called:<br></div><div><a href="https://godbolt.org/z/WFp2rm" rel="noreferrer noreferrer" target="_blank">https://godbolt.org/z/WFp2rm</a> </div><div><br></div><div>The loop is in method work; the virtual call is to method id.</div><div><br></div><div>We can see that method work gets inlined, but inside the loop, there are always two pointer dereferences:</div><div><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,0,255)">mov</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">     </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(72,100,170)">rax</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">, </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,128,128)">qword</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, ""> </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,128,128)">ptr</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, ""> [</span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(72,100,170)">r14</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">]</span></div><div><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,0,255)">call</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">    </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,128,128)">qword</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, ""> </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,128,128)">ptr</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, ""> [</span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(72,100,170)">rax</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">]</span></div><div><br></div><div>Since the object referred to by b, never changes to a different object, this could(at least in this case), be cached.</div><div><br></div><div>My assembly might be rusty, but before the loop, we could have:</div><div><div><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,0,255)">mov</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">     </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(72,100,170)">r13</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">, </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,128,128)">qword</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, ""> </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,128,128)">ptr</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, ""> [</span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(72,100,170)">r14</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">]</span></div><div><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,0,255)">mov</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">     </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(72,100,170)">r13</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">, </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,128,128)">qword</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, ""> </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,128,128)">ptr</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, ""> [</span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(72,100,170)">r13</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">]</span>  <span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, ""><br></span></div><div><br></div><div>and inside the loop we would only have: <br></div><div><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,0,255)"><br></span></div><div><span style="background-color:rgb(255,255,254);font-family:"Consolas, ";color:rgb(0,0,255)">call</span><span style="background-color:rgb(255,255,254);color:rgb(0,0,0);font-family:"Consolas, "">    </span><span style="background-color:rgb(255,255,254);font-family:"Consolas, ""><font color="#008080">r13</font></span></div></div><div><span style="background-color:rgb(255,255,254);font-family:"Consolas, ""><font color="#008080"><br></font></span></div><div><b>My questions are:</b><br></div><div>Do we have a mechanism in C++ to explicitly store the result of the lookup in the vtable without additional overhead? Some sort of cache for this result so that we do not do the same computation over and over again? I'm specifically looking for this solution, not alternatives to dynamic polymorphism like CRTP or std::variant. I couldn't find one.</div><div>For functional programming style "pure functions", we would have:</div><div>    int res = pure_function();</div><div>    while(true) use(res);</div><div>instead of</div><div>    while(true) use(pure_function());</div><div><br></div><div>Is there anything in the C++ standard that prevents such an optimization?</div></div></blockquote></div></div><div dir="auto"><br></div><div dir="auto">The optimization is valid, and clang performs it under -fstrict-vtable-pointers (<a href="https://godbolt.org/z/SrlUC8" target="_blank" rel="noreferrer">https://godbolt.org/z/SrlUC8</a>). Unfortunately, there are still some cases where this optimization can regress performance (the annotations that the frontend inserts to enable the optimization can get in the way of other transformations), so it's not enabled by default yet.</div></div></blockquote></div></div><div dir="auto"><br></div><div dir="auto">Actually, I think the above performance concern is way out of date. I think we decided that we are happy with the performance delta (it improves a lot more than it regresses) and the remaining blocker for enabling it by default was ensuring LLVM IR ABI compatibility (especially when performing LTO between compilations with the feature turned off and compilations with it turned on).</div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>How would we identify the cases in which such an optimization is possible and the ones in which it is not?</div><div><br></div><div>Are there any other reasons for which such an optimization would not be desired?</div><div><br></div><div><br></div><div>N.B.: I realize the last two questions might be difficult to answer, but could you at least point me in the right direction for investigating this myself?</div><div><br></div><div>Thanks,</div><div>Ninu.</div></div>
_______________________________________________<br>
cfe-dev mailing list<br>
<a href="mailto:cfe-dev@lists.llvm.org" rel="noreferrer noreferrer" target="_blank">cfe-dev@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev" rel="noreferrer noreferrer noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev</a><br>
</blockquote></div></div></div>
</blockquote></div></div></div>