При переходе с VS2005 на VS2010 мы заметили снижение производительности, которое, по-видимому, было вызвано дополнительными копиями функтора.
Следующий код иллюстрирует проблему. Важно иметь карту, где само значение является набором. И на карте, и на наборе мы определили функтор сравнения (который приведен в качестве примера в примере).
#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;
}
Если я скомпилирую этот код с VS2005, я получу следующий вывод:
--- 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
Если я скомпилирую это с VS2010, я получу следующий вывод:
--- 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
Вывод для первого оператора (построения карты) идентичен.
Вывод для второго оператора (создание первого элемента на карте и получение ссылки на него) в случае VS2010 намного больше:
- Копировать конструктор функтора: 10 раз против 8 раз
- Назначение функтора: 2 раза против 0 раз
- Деструктор функтора: 10 раз против 8 раз
Мои вопросы:
- Почему STL копирует функтор? Разве этого не достаточно, чтобы создать его один раз для каждого экземпляра набора?
- Почему функтор построен больше в случае VS2010, чем в случае VS2005? (не проверял VS2008)
- И почему он назначается два раза в VS2010, а не в VS2005?
- Есть ли уловки, чтобы избежать копирования функторов?
Я видел похожий вопрос в Предотвращение ненужных копий объектов функтора C ++ , но я не уверен, что это тот же вопрос.