3

我之前问过一个问题为什么 dynamic_cast 邪恶或不邪恶?答案让我写了一些关于性能的代码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:Linux
CPU:Intel(R) Xeon(R) CPU E5607  @ 2.27GHz  (4 cores)
4

2 回答 2

1

您确实编写了正确的代码,尽管我不会将类型硬编码为 Cat。为了安全起见,您可以使用命令行参数来决定是构建猫还是狗(您也应该实现)。也尝试禁用优化,以查看它是否发挥了重要作用。

最后,需要注意一点。分析并不像在计算机上进行测量那么简单,因此您必须意识到您所做的只是到目前为止。它确实给了你一个想法,但不要认为你得到了任何包罗万象的答案。

于 2013-09-09T06:55:29.273 回答
-1

我将重新编写我的帖子。

您的代码是正确的,并且编译得很好。

由于虚方法和dynamic_cast操作符是相关的问题,请从wiki查看此信息,希望对您有用。

维基:

与非虚拟调用相比,虚拟调用至少需要额外的索引取消引用,有时还需要“修复”添加,后者只是跳转到编译指针。因此,调用虚函数本质上比调用非虚函数慢。1996 年进行的一项实验表明,大约 6-13% 的执行时间用于简单地分派到正确的函数,尽管开销可能高达 50%。 [4] 由于更大的缓存和更好的分支预测,虚拟功能的成本在现代 CPU 架构上可能不会那么高。

此外,在不使用 JIT 编译的环境中,通常不能内联虚函数调用。虽然编译器可以用例如每个内联体的条件执行来替换查找和间接调用,但这种优化并不常见。

为了避免这种开销,只要可以在编译时解析调用,编译器通常会避免使用 vtables。

因此,上面对 f1 的调用可能不需要 vtable 查找,因为编译器可能会知道此时 d 只能保存一个 D,而 D 不会覆盖 f1。或者编译器(或优化器)可能能够检测到程序中的任何地方都没有覆盖 f1 的 B1 子类。对 B1::f1 或 B2::f2 的调用可能不需要 vtable 查找,因为明确指定了实现(尽管它仍然需要 'this'-pointer fixup)。

此外,您可能知道,当您在类中声明虚拟方法时,取决于实现,但几乎总是,您的编译器会隐式地将虚拟方法表作为新成员添加到您的类中,因此该类的每​​个实例将占用更多内存空间,在有 vm 和没有它的情况下尝试 sizeof。

于 2013-09-09T06:43:24.823 回答