Politics versus polymorphic speed

I read that using a policy class for a function that will be called in a narrow loop is much faster than using a polymorphic function. However, I am setting up this demo, and time indicates that this is exactly the opposite !? The policy version is 2-3x longer than the polymorphic version.

#include <iostream>

#include <boost/timer.hpp>

// Policy version
template < typename operation_policy>
class DoOperationPolicy : public operation_policy
{
  using operation_policy::Operation;

public:
  void Run(const float a, const float b)
  {
    Operation(a,b);
  }
};

class OperationPolicy_Add
{
protected:
  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;
  }
};

int main()
{
  boost::timer timer;

  unsigned int numberOfIterations = 1e7;

  DoOperationPolicy<OperationPolicy_Add> policy_operation;
  for(unsigned int i = 0; i < numberOfIterations; ++i)
    {
    policy_operation.Run(1,2);
    }
  std::cout << timer.elapsed() << " seconds." << std::endl;
  timer.restart();

  DoOperation* polymorphic_operation = new OperationAdd;
  for(unsigned int i = 0; i < numberOfIterations; ++i)
    {
    polymorphic_operation->Run(1,2);
    }
  std::cout << timer.elapsed() << " seconds." << std::endl;

}

Is there something wrong with the demo? Or is it simply not true that politics should be faster?

+5
source share
5 answers

Your test does not make sense (sorry).

Creating real tests is difficult, unfortunately, because compilers are very smart.

What to look for here:

  • devirtualization: , , , , polymorphic_operation OperationAdd , , OperationAdd::Run
  • inline: , .
  • " ": , , , , , .

, :

int main()
{
  boost::timer timer;

  std::cout << timer.elapsed() << " seconds." << std::endl;

  timer.restart();

  DoOperation* polymorphic_operation = new OperationAdd;

  std::cout << timer.elapsed() << " seconds." << std::endl;
}

, , ...

, :

, DoOperation& Get(), cpp: DoOperation& Get() { static OperationAdd O; return O; }.

( , ): , .


:

// 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& GetInline() {
    static OperationAdd O;
    return O;
  }

  DoOperation& GetOutOfLine() {
    static OperationAddOutOfLine O;
    return O;
  }

} // namespace so8746025

// test.cpp
#include <iostream>

#include <boost/timer.hpp>

namespace so8746025 {

  // Policy version
  template < typename operation_policy>
  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& GetInline();
  DoOperation& GetOutOfLine();

} // namespace so8746025

using namespace so8746025;

int main()
{
  unsigned int numberOfIterations = 1e8;

  DoOperationPolicy<OperationPolicy_Add> policy;

  OperationAdd stackInline;
  DoOperation& virtualInline = GetInline();

  OperationAddOutOfLine stackOutOfLine;
  DoOperation& virtualOutOfLine = GetOutOfLine();


  boost::timer timer;

  float result = 0;

  for(unsigned int i = 0; i < numberOfIterations; ++i)  {
    result += policy.Run(1,2);
  }
  std::cout << "Policy: " << timer.elapsed() << " seconds (" << result << ")" << std::endl;


  timer.restart();
  result = 0;
  for(unsigned int i = 0; i < numberOfIterations; ++i)
  {
    result += stackInline.Run(1,2);
  }
  std::cout << "Stack Inline: " << timer.elapsed() << " seconds (" << result << ")" << std::endl;

  timer.restart();
  result = 0;
  for(unsigned int i = 0; i < numberOfIterations; ++i)
  {
    result += virtualInline.Run(1,2);
  }
  std::cout << "Virtual Inline: " << timer.elapsed() << " seconds (" << result << ")" << std::endl;

  timer.restart();
  result = 0;
  for(unsigned int i = 0; i < numberOfIterations; ++i)
  {
    result += stackOutOfLine.Run(1,2);
  }
  std::cout << "Stack Out Of Line: " << timer.elapsed() << " seconds (" << result << ")" << std::endl;

  timer.restart();
  result = 0;
  for(unsigned int i = 0; i < numberOfIterations; ++i)
  {
    result += virtualOutOfLine.Run(1,2);
  }
  std::cout << "Virtual Out Of Line: " << timer.elapsed() << " seconds (" << result << ")" << std::endl;

}

:

$ 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)

+ inline .

+4

FWIW

  • , mixn
  • volatile, (, / - , ).
  • -O3 gcc

:

DoDirect: 3.4 seconds.
Policy: 3.41 seconds.
Polymorphic: 3.4 seconds.

: . , GCC DoOperation *, DoOperationAdd - vtable:)

reallife , , volatile.

DoDirect: 6.71089e+07 in 1.12 seconds.
Policy: 6.71089e+07 in 1.15 seconds.
Polymorphic: 6.71089e+07 in 3.38 seconds.

, ; , + ( ). , "", (.. )

CODE

#include <iostream>

#include <boost/timer.hpp>

// Direct version
struct DoDirect {
    static float Run(const float a, const float b) { return a + b; }
};

// Policy version
template <typename operation_policy>
struct DoOperationPolicy {
    float Run(const float a, const float b) const {
        return operation_policy::Operation(a,b);
    }
};

struct OperationPolicy_Add {
    static float Operation(const float a, const float b) {
        return a + b;
    }
};

// Polymorphic version
struct DoOperation {
    virtual float Run(const float a, const float b) const = 0;
};

struct OperationAdd  : public DoOperation { 
    float Run(const float a, const float b) const { return a + b; } 
};

int main(int argc, const char *argv[])
{
    boost::timer timer;

    const unsigned long numberOfIterations = 1<<30ul;

    volatile float result = 0;
    for(unsigned long i = 0; i < numberOfIterations; ++i) {
        result += DoDirect::Run(1,2);
    }
    std::cout << "DoDirect: " << result << " in " << timer.elapsed() << " seconds." << std::endl;
    timer.restart();

    DoOperationPolicy<OperationPolicy_Add> policy_operation;
    for(unsigned long i = 0; i < numberOfIterations; ++i) {
        result += policy_operation.Run(1,2);
    }
    std::cout << "Policy: " << result << " in " << timer.elapsed() << " seconds." << std::endl;
    timer.restart();

    result = 0;
    DoOperation* polymorphic_operation = new OperationAdd;
    for(unsigned long i = 0; i < numberOfIterations; ++i) {
        result += polymorphic_operation->Run(1,2);
    }
    std::cout << "Polymorphic: " << result << " in " << timer.elapsed() << " seconds." << std::endl;

}
+3

. , , , , , , .

+2

,

  • ( , )
  • -, ( - )
+2

, :

float Run(const float a, const float b)
{
  return Operation(a,b);
}

-, , , :

int main()
{
  unsigned int numberOfIterations = 1e9;
  float answer = 0.0;

  boost::timer timer;
  DoOperationPolicy<OperationPolicy_Add> policy_operation;
  for(unsigned int i = 0; i < numberOfIterations; ++i)
  {
    answer += policy_operation.Run(1,2);
  }
  std::cout << "Policy got " << answer << " in " << timer.elapsed() << " seconds" << std::endl;

  answer = 0.0;
  timer.restart();
  DoOperation* polymorphic_operation = new OperationAdd;
  for(unsigned int i = 0; i < numberOfIterations; ++i)
  {
    answer += polymorphic_operation->Run(1,2);
  }
  std::cout << "Polymo got " << answer << " in " << timer.elapsed() << " seconds" << std::endl;

  return 0;
}

g++ 4.1.2:

Policy got 6.71089e+07 in 13.75 seconds
Polymo got 6.71089e+07 in 7.52 seconds

-O3 g++ 4.1.2:

Policy got 6.71089e+07 in 1.18 seconds
Polymo got 6.71089e+07 in 3.23 seconds

, .

+1

All Articles