众所周知的避免dynamic_cast缓慢的解决方案?

时间:2012-09-10 01:28:33

标签: c++ performance dynamic-cast

我需要运行时多态,所以我使用了dynamic_cast 但现在我有两个问题 - dynamic_cast 非常慢! (向下滚动以获得基准。)

长话短说,我最终用这种方式解决问题,使用static_cast

struct Base
{
    virtual ~Base() { }
    virtual int type_id() const = 0;

    template<class T>
    T *as()
    { return this->type_id() == T::ID ? static_cast<T *>(this) : 0; }

    template<class T>
    T const *as() const
    { return this->type_id() == T::ID ? static_cast<T const *>(this) : 0; }
};

struct Derived : public Base
{
    enum { ID = __COUNTER__ };  // warning: can cause ABI incompatibility
    int type_id() const { return ID; }
};

int main()
{
    Base const &value = Derived();
    Derived const *p = value.as<Derived>();  // "static" dynamic_cast
}

但我肯定不是第一个遇到这个问题的人,所以我认为值得一提:

有没有一个众所周知的模式/库可以用来解决这个问题,而不是想出这样的家庭式解决方案?


示例基准

要了解我正在谈论的内容,请尝试下面的代码 - dynamic_cast大约 15次比我机器上的virtual调用慢一点 (110 ms。对1620 ms。使用下面的代码):

#include <cstdio>
#include <ctime>

struct Base { virtual unsigned vcalc(unsigned i) const { return i * i + 1; } };
struct Derived1 : public Base 
{ unsigned vcalc(unsigned i) const { return i * i + 2; } };
struct Derived2 : public Derived1
{ unsigned vcalc(unsigned i) const { return i * i + 3; } };

int main()
{
    Base const &foo = Derived2();
    size_t const COUNT = 50000000;
    {
        clock_t start = clock();
        unsigned n = 0;
        for (size_t i = 0; i < COUNT; i++)
            n = foo.vcalc(n);
        printf("virtual call: %d ms (result: %u)\n",
            (int)((clock() - start) * 1000 / CLOCKS_PER_SEC), n);
        fflush(stdout);
    }
    {
        clock_t start = clock();
        unsigned n = 0;
        for (size_t i = 0; i < COUNT; i++)
            n = dynamic_cast<Derived1 const &>(foo).vcalc(n);
        printf("virtual call after dynamic_cast: %d ms (result: %u)\n",
            (int)((clock() - start) * 1000 / CLOCKS_PER_SEC), n);
        fflush(stdout);
    }
    return 0;
}

当我只删除单词virtual并将dynamic_cast更改为static_cast时,我的运行时间为79毫秒 - 因此虚拟调用仅比静态调用慢〜 25%!

2 个答案:

答案 0 :(得分:3)

dynamic_cast的大部分用法都可以替换为双重调度(又名。visitor pattern)。这将相当于两个虚拟调用,您的基准测试仍然比dynamic_cast快7.5倍。

答案 1 :(得分:2)

您可能对此恒定时间实施感兴趣:http://www.stroustrup.com/isorc2008.pdf

另请注意,在特定约束条件下可能会简化许多向上转换 - 例如,如果不使用多重继承,仅使用浅层继承,或者保证类型没有共同的祖先,则某些评估可能会被短路并且无需执行详尽的评估(由dynamic_cast提供)。

像往常一样,根据您的供应商针对给定用例和实际类层次结构的实现来分析您的实现。