Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Your benchmark is meaningless (sorry).</p> <p>Making real benchmarks is hard, unfortunately, as compilers are very clever.</p> <p>Things to look for here:</p> <ul> <li>devirtualization: the polymorphic call is expected to be slower because it is supposed to be virtual, but here the compiler can realize than <code>polymorphic_operation</code> is necessarily a <code>OperationAdd</code> and thus directly call <code>OperationAdd::Run</code> without invoking runtime dispatch</li> <li>inlining: since the compiler has access to the methods body, it can inline them, and avoid the function calls altogether.</li> <li>"dead store removal": values that are not used need not be stored, and the computations that lead to them and do not provoke side-effects can be avoided entirely.</li> </ul> <p>Indeed, your entire benchmark code can be optimized to:</p> <pre><code>int main() { boost::timer timer; std::cout &lt;&lt; timer.elapsed() &lt;&lt; " seconds." &lt;&lt; std::endl; timer.restart(); DoOperation* polymorphic_operation = new OperationAdd; std::cout &lt;&lt; timer.elapsed() &lt;&lt; " seconds." &lt;&lt; std::endl; } </code></pre> <p>Which is when you realize that you are not timing what you'd like to...</p> <p>In order to make your benchmark meaningful you need to:</p> <ul> <li>prevent devirtualization</li> <li>force side-effects</li> </ul> <p>To prevent devirtualization, just declare a <code>DoOperation&amp; Get()</code> function, and then in another cpp file: <code>DoOperation&amp; Get() { static OperationAdd O; return O; }</code>.</p> <p>To force side-effects (only necessary if the methods are inlined): return the value and accumulate it, then display it.</p> <hr> <p>In action using this program:</p> <pre><code>// test2.cpp namespace so8746025 { class DoOperation { public: virtual float Run(const float a, const float b) = 0; }; class OperationAdd : public DoOperation { public: float Run(const float a, const float b) { return a + b; } }; class OperationAddOutOfLine: public DoOperation { public: float Run(const float a, const float b); }; float OperationAddOutOfLine::Run(const float a, const float b) { return a + b; } DoOperation&amp; GetInline() { static OperationAdd O; return O; } DoOperation&amp; GetOutOfLine() { static OperationAddOutOfLine O; return O; } } // namespace so8746025 // test.cpp #include &lt;iostream&gt; #include &lt;boost/timer.hpp&gt; namespace so8746025 { // Policy version template &lt; typename operation_policy&gt; struct DoOperationPolicy { float Run(const float a, const float b) { return operation_policy::Operation(a,b); } }; struct OperationPolicy_Add { static float Operation(const float a, const float b) { return a + b; } }; // Polymorphic version class DoOperation { public: virtual float Run(const float a, const float b) = 0; }; class OperationAdd : public DoOperation { public: float Run(const float a, const float b) { return a + b; } }; class OperationAddOutOfLine: public DoOperation { public: float Run(const float a, const float b); }; DoOperation&amp; GetInline(); DoOperation&amp; GetOutOfLine(); } // namespace so8746025 using namespace so8746025; int main() { unsigned int numberOfIterations = 1e8; DoOperationPolicy&lt;OperationPolicy_Add&gt; policy; OperationAdd stackInline; DoOperation&amp; virtualInline = GetInline(); OperationAddOutOfLine stackOutOfLine; DoOperation&amp; virtualOutOfLine = GetOutOfLine(); boost::timer timer; float result = 0; for(unsigned int i = 0; i &lt; numberOfIterations; ++i) { result += policy.Run(1,2); } std::cout &lt;&lt; "Policy: " &lt;&lt; timer.elapsed() &lt;&lt; " seconds (" &lt;&lt; result &lt;&lt; ")" &lt;&lt; std::endl; timer.restart(); result = 0; for(unsigned int i = 0; i &lt; numberOfIterations; ++i) { result += stackInline.Run(1,2); } std::cout &lt;&lt; "Stack Inline: " &lt;&lt; timer.elapsed() &lt;&lt; " seconds (" &lt;&lt; result &lt;&lt; ")" &lt;&lt; std::endl; timer.restart(); result = 0; for(unsigned int i = 0; i &lt; numberOfIterations; ++i) { result += virtualInline.Run(1,2); } std::cout &lt;&lt; "Virtual Inline: " &lt;&lt; timer.elapsed() &lt;&lt; " seconds (" &lt;&lt; result &lt;&lt; ")" &lt;&lt; std::endl; timer.restart(); result = 0; for(unsigned int i = 0; i &lt; numberOfIterations; ++i) { result += stackOutOfLine.Run(1,2); } std::cout &lt;&lt; "Stack Out Of Line: " &lt;&lt; timer.elapsed() &lt;&lt; " seconds (" &lt;&lt; result &lt;&lt; ")" &lt;&lt; std::endl; timer.restart(); result = 0; for(unsigned int i = 0; i &lt; numberOfIterations; ++i) { result += virtualOutOfLine.Run(1,2); } std::cout &lt;&lt; "Virtual Out Of Line: " &lt;&lt; timer.elapsed() &lt;&lt; " seconds (" &lt;&lt; result &lt;&lt; ")" &lt;&lt; std::endl; } </code></pre> <p>We get:</p> <pre><code>$ gcc --version gcc (GCC) 4.3.2 $ ./testR Policy: 0.17 seconds (6.71089e+07) Stack Inline: 0.17 seconds (6.71089e+07) Virtual Inline: 0.52 seconds (6.71089e+07) Stack Out Of Line: 0.6 seconds (6.71089e+07) Virtual Out Of Line: 0.59 seconds (6.71089e+07) </code></pre> <p>Note the subtle difference between devirtualization + inline and the absence of devirtualization.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload