dynamic_cast的性能

时间:2013-09-09 03:54:20

标签: c++ rtti dynamic-cast

我之前问了一个问题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)

2 个答案:

答案 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,没有它。