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 http://stackoverflow.com/questions/2216041/prevent-unnecessary-copies-of-c-functor-objects but I'm not sure that's the same question.
Thanks in advance,
Patrick