简体   繁体   English

避免dynamic_cast缓慢的知名解决方案?

[英]Well-known solution for avoiding the slowness of dynamic_cast?

I needed run-time polymorphism, so I used dynamic_cast . 我需要运行时多态性,所以我使用dynamic_cast
But now I had two problems -- dynamic_cast was extremely slow ! 但是现在我有两个问题dynamic_cast 非常慢 (Scroll down for benchmark.) (向下滚动以获取基准。)

Long story short, I ended up solving the problem this way, using static_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
}

But I'm surely not the first person to run into this problem, so I thought it might be worth asking: 但是我肯定不是第一个遇到这个问题的人,所以我认为可能值得一问:

Instead of coming up with a home-baked solution like this, is there a well-known pattern/library I can use for solving this problem in the future? 除了提供像这样的自制解决方案之外,还有没有我将来可以用来解决此问题的知名模式/库?


Sample benchmark 样本基准

To get a feel for what I'm talking about, try the code below -- dynamic_cast was approximately 15 times slower than a mere virtual call on my machine (110 ms. against 1620 ms. with the code below): 要了解我在说什么,请尝试以下代码dynamic_cast大约比我的机器上仅进行virtual调用的速度慢15倍 (110毫秒与1620毫秒,使用以下代码):

#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;
}

When I simply remove the word virtual and change dynamic_cast to static_cast , I get a 79 ms running time -- so a virtual call is only slower than a static call by ~25%! 当我简单地删除单词virtual并将dynamic_cast更改为static_cast ,我得到了79毫秒的运行时间-因此虚拟通话仅比静态通话慢25%左右!

Most uses of dynamic_cast can be replaced with double dispatch (aka. visitor pattern ). dynamic_cast大多数用法可以替换为double dispatch(又称为visitor pattern )。 That would amount to two virtual calls, which by your benchmark is still 7.5 times faster than dynamic_cast . 这相当于两个虚拟调用,根据您的基准,这仍然比dynamic_cast快7.5倍。

You may be interested in this constant time implementation: http://www.stroustrup.com/isorc2008.pdf 您可能对此固定时间的实施感兴趣: http : //www.stroustrup.com/isorc2008.pdf

Also note that many upcasts may be simplified under specific constraints -- for example, if you do not use multiple inheritance, use only shallow inheritance, or otherwise guarantee that the type will have no common ancestors, some evaluations can be short circuited and an exhaustive evaluation (as provided by dynamic_cast ) need not be performed. 另请注意,在特定的约束下,许多上流操作可能会得到简化-例如,如果您不使用多重继承,仅使用浅继承或以其他方式保证该类型没有公共祖先,则某些评估可能会被短路并且详尽无遗。无需执行评估(由dynamic_cast提供)。

As usual, profile your implementation against your vendor's implementation for your given use case and actual class hierarchies. 与往常一样,针对给定的用例和实际的类层次结构,将实现与供应商的实现进行比较。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM