我之前问了一个问题Why is dynamic_cast evil or not ? 答案让我写了一些关于dynamic_cast
性能的代码如下。我编译并测试了dynamic_cast
消耗的时间略大一些比没有dynamic_cast
的那个。我没有看到dynamic_cast
的证据是耗时的。我写的是正确的代码吗?
代码是:
class Animal
{
public:
virtual ~Animal(){};
};
class Cat : public Animal
{
public:
std::string param1;
std::string param2;
std::string param3;
std::string param4;
std::string param5;
int param6;
int param7;
};
bool _process(Cat* cat)
{
cat->param1 = "abcde";
cat->param2 = "abcde";
cat->param3 = "abcde";
cat->param4 = "abcde";
cat->param5 = "abcde";
cat->param6 = 1;
cat->param7 = 2;
return true;
}
bool process(Animal *ptr)
{
Cat *cat = dynamic_cast<Cat*>(ptr);
if (cat == NULL)
{
return false;
}
_process(cat);
return true;
}
int main(int argc, char* argv[])
{
/*
argv[1] : object num
*/
if (argc != 2)
{
std::cout << "Error: invalid argc " << std::endl;
return -1;
}
int obj_num = atoi(argv[1]);
if (obj_num <= 0)
{
std::cout << "Error: object num" << std::endl;
}
int c = 0;
for (; c < obj_num; c++)
{
Cat cat;
#ifdef _USE_CAST
if (!process(&cat))
{
std::cout << "Error: failed to process " << std::endl;
return -3;
}
#else
if (!_process(&cat))
{
std::cout << "Error: failed to process " << std::endl;
return -3;
}
#endif
}
return 0;
}
使用以下方法编译它:
g++ -D_USE_CAST -o dynamic_cast_test dynamic_cast_benchmark.c
g++ -o dynamic_cast_no_test dynamic_cast_benchmark.c
使用num执行它们,即1,10,100 ......:
$time ./dynamic_cast_test num
$time ./dynamic_cast_no_test num
结果:
dynamic_cast non_dynamic_cast
num 10,000
real 0m0.010s real 0m0.008s
user 0m0.006s user 0m0.006s
sys 0m0.001s sys 0m0.001s
100,000
real 0m0.059s real 0m0.056s
user 0m0.054s user 0m0.054s
sys 0m0.001s sys 0m0.001s
1,000,000
real 0m0.523s real 0m0.519s
user 0m0.517s user 0m0.511s
sys 0m0.001s sys 0m0.004s
10,000,000
real 0m6.050s real 0m5.126s
user 0m5.641s user 0m4.986s
sys 0m0.036s sys 0m0.019s
100,000,000
real 0m52.962s real 0m51.178s
user 0m51.697s user 0m50.226s
sys 0m0.173s sys 0m0.092s
硬件和硬件OS:
OS:Linux
CPU:Intel(R) Xeon(R) CPU E5607 @ 2.27GHz (4 cores)
答案 0 :(得分:1)
你确实写了正确的代码,虽然我不会将类型硬编码为Cat。你可以,只是为了安全起见,使用命令行参数来决定是否建造一只猫或狗,(你也应该实施)。尝试禁用优化,以查看它是否发挥了重要作用。
最后,要注意一点。分析并不像在计算机上进行测量那么简单,因此您必须意识到您所做的只是带您到目前为止。它确实给了你一个想法,不要以为你得到任何包罗万象的答案。
答案 1 :(得分:-1)
我将重新制定我的职位。
您的代码是正确的,编译得很好。
由于虚方法和dynamic_cast运算符是相关问题,请从wiki检查此信息,希望它有用。
维基:
虚拟呼叫至少需要一个额外的索引取消引用,和 与非虚拟呼叫相比,有时会增加“修正” 只需跳转到编译指针即可。因此,调用虚拟 函数本质上比调用非虚函数慢。一个 1996年完成的实验表明约占6-13% 执行时间花在简单调度到正确的函数上, 虽然开销可高达50%。[4]虚拟的成本 由于太多,现代CPU架构上的功能可能不是那么高 更大的缓存和更好的分支预测。
此外,在不使用JIT编译的环境中, 虚函数调用通常不能内联。而编译器 可以用例如a替换查找和间接调用 每个内联体的条件执行,这样的优化不是 常见的。
为了避免这种开销,编译器通常会避免每次都使用vtable 调用可以在编译时解决。
因此,上面对f1的调用可能不需要vtable查找,因为 编译器可能会告诉d此时只能持有D, 和D不会覆盖f1。或者编译器(或优化器)可能 检测程序中的任何位置都没有B1的子类 覆盖f1。对B1 :: f1或B2 :: f2的调用可能不会 需要vtable查找,因为指定了实现 显式(虽然它仍然需要'this'指针修复)。
另外,正如您可能知道的那样,当您在类中声明虚方法时,依赖于实现但几乎总是如此,您的编译器将隐式地将虚方法表添加为您的类的新方法,因此该类的每个实例都将占用更多的内存空间,在类上使用vm尝试sizeof,没有它。