1

When moving from VS2005 to VS2010 we noticed a performance decrease, which seemed to be caused by additional copies of a functor.

The following code illustrates the problem. It is essential to have a map where the value itself is a set. On both the map and the set we defined a comparison functor (which is templated in the example).

#include <iostream>
#include <map>
#include <set>

class A
{
public:
   A(int i, char c) : m_i(i), m_c(c)
   {
   std::cout << "Construct object " << m_c << m_i << std::endl;
   }

   A(const A &a) : m_i(a.m_i), m_c(a.m_c)
   {
   std::cout << "Copy object " << m_c << m_i << std::endl;
   }

   ~A()
   {
   std::cout << "Destruct object " << m_c << m_i << std::endl;
   }

   void operator= (const A &a)
   {
   m_i = a.m_i;
   m_c = a.m_c;
   std::cout << "Assign object " << m_c << m_i << std::endl;
   }

   int m_i;
   char m_c;

};

class B : public A
{
public:
   B(int i) : A(i, 'B') { }

   static const char s_c = 'B';
};

class C : public A
{
public:
   C(int i) : A(i, 'C') { }

   static const char s_c = 'C';
};

template <class X>
class compareA
{
public:
   compareA() : m_i(999)
   {
   std::cout << "Construct functor " << X::s_c << m_i << std::endl;
   }

   compareA(const compareA &a) : m_i(a.m_i)
   {
   std::cout << "Copy functor " << X::s_c << m_i << std::endl;
   }

   ~compareA()
   {
   std::cout << "Destruct functor " << X::s_c << m_i << std::endl;
   }

   void operator= (const compareA &a)
   {
   m_i = a.m_i;
   std::cout << "Assign functor " << X::s_c << m_i << std::endl;
   }

   bool operator() (const X &x1, const X &x2) const
   {
   std::cout << "Comparing object " << x1.m_i << " with " << x2.m_i << std::endl;
   return x1.m_i < x2.m_i;
   }

private:
   int m_i;
};


typedef std::set<C, compareA<C> > SetTest;
typedef std::map<B, SetTest, compareA<B> >  MapTest;

int main()
   {
   int i = 0;
   std::cout << "--- " << i++ << std::endl;
   MapTest mapTest;
   std::cout << "--- " << i++ << std::endl;
   SetTest &setTest = mapTest[0];
   std::cout << "--- " << i++ << std::endl;
   }

If I compile this code with VS2005 I get the following output:

--- 0
Construct functor B999
Copy functor B999
Copy functor B999
Destruct functor B999
Destruct functor B999
--- 1
Construct object B0
Construct functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Destruct functor C999
Destruct object B0
Destruct functor C999
Destruct object B0
--- 2

If I compile this with VS2010, I get the following output:

--- 0
Construct functor B999
Copy functor B999
Copy functor B999
Destruct functor B999
Destruct functor B999
--- 1
Construct object B0
Construct functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy functor C999
Assign functor C999
Assign functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy functor C999
Assign functor C999
Assign functor C999
Destruct functor C999
Destruct functor C999
Destruct object B0
Destruct functor C999
Destruct object B0
--- 2

The output for the first statement (constructing the map) is identical.

The output for the second statement (creating the first element in the map and getting a reference to it), is much bigger in the VS2010 case:

  • Copy constructor of functor: 10 times vs. 8 times
  • Assignment of functor: 2 times vs. 0 times
  • Destructor of functor: 10 times vs. 8 times

My questions are:

  • Why does the STL copy a functor? Isn't it enough to construct it once for every instantiation of the set?
  • Why is the functor constructed more in the VS2010 case than in the VS2005 case? (didn't check VS2008)
  • And why is it assigned two times in VS2010 and not in VS2005?
  • Are there any tricks to avoid the copy of functors?

I saw a similar question at Prevent unnecessary copies of C++ functor objects but I'm not sure that's the same question.

Community
  • 1
  • 1
Patrick
  • 23,217
  • 12
  • 67
  • 130
  • Both of them on Release build with the exact same compiler settings/optimization flags? – dirkgently Mar 15 '10 at 07:57
  • Yep. Compiled 3 times on VS2005 and 2010. One time with no optimization flags, one time with /Ox, one time with /Od. In all results I saw that VS2010 copied and assigned the functor more times than VS2005. – Patrick Mar 15 '10 at 14:48
  • tried with /O2 as well? I'm not sure that /Ox is a strict superset of /O2. Apart from this, are you sure the functor copying is the cause of your slowdown? As far as I can see, the functor contains just an int, so it should be pretty trivial to copy. Finally, the STL doesn't *require* the functor to be copied. It merely allows it. And it does this because that might in some cases enable additional optimizations. As for avoiding copies, you could experiment with either passing 1) rvalue references or 2) plain value objects (instead of const refs). – jalf May 31 '10 at 16:48

0 Answers0