当从 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 次 vs. 8 次
- 函子的赋值:2次vs.0次
- 函子的析构函数:10 次 vs. 8 次
我的问题是:
- 为什么 STL 复制函子?为集合的每个实例化构造一次还不够吗?
- 为什么在 VS2010 案例中构造的函子比在 VS2005 案例中更多?(没有检查VS2008)
- 为什么它在VS2010中分配了两次而不是在VS2005中?
- 有什么技巧可以避免仿函数的复制吗?
我在防止不必要的 C++ 仿函数对象副本中看到了一个类似的问题,但我不确定这是同一个问题。