【问题标题】:Circumventing RTTI on legacy code在遗留代码上规避 RTTI
【发布时间】:2014-08-25 23:15:55
【问题描述】:

我一直在寻找一种方法来解决动态转换类型检查的缓慢问题。在你开始说我应该重新设计所有东西之前,让我告诉你设计是 5 年前决定的。我无法修复之后出现的所有 400,000 行代码(我希望可以),但我可以进行一些更改。我已经对类型识别进行了这个小测试:

#include <iostream>
#include <typeinfo>
#include <stdint.h>
#include <ctime>

using namespace std;

#define ADD_TYPE_ID \
    static intptr_t type() { return reinterpret_cast<intptr_t>(&type); }\
    virtual intptr_t getType() { return type(); }

struct Base
{
    ADD_TYPE_ID;
};

template <typename T>
struct Derived : public Base
{
    ADD_TYPE_ID;
};

int main()
{
    Base* b = new Derived<int>();
    cout << "Correct Type: " << (b->getType() == Derived<int>::type()) << endl; // true
    cout << "Template Type: " << (b->getType() == Derived<float>::type()) << endl; // false
    cout << "Base Type: " << (b->getType() == Base::type()) << endl; // false

    clock_t begin = clock();
    {
        for (size_t i = 0; i < 100000000; i++)
        {
            if (b->getType() == Derived<int>::type())
                Derived <int>* d = static_cast<Derived<int>*> (b);
        }
    }
    clock_t end = clock();
    double elapsed = double(end - begin) / CLOCKS_PER_SEC;

    cout << "Type elapsed: " << elapsed << endl;

    begin = clock();
    {
        for (size_t i = 0; i < 100000000; i++)
        {
            Derived<int>* d = dynamic_cast<Derived<int>*>(b);
            if (d);
        }
    }
    end = clock();
    elapsed = double(end - begin) / CLOCKS_PER_SEC;

    cout << "Type elapsed: " << elapsed << endl;

    begin = clock();
    {
        for (size_t i = 0; i < 100000000; i++)
        {
            Derived<int>* d = dynamic_cast<Derived<int>*>(b);
            if ( typeid(d) == typeid(Derived<int>*) )
                static_cast<Derived<int>*> (b);
        }
    }
    end = clock();
    elapsed = double(end - begin) / CLOCKS_PER_SEC;

    cout << "Type elapsed: " << elapsed << endl;

   return 0;
}

似乎使用类 id(上面的第一次解决方案)将是在运行时进行类型检查的最快方法。 这会导致线程出现任何问题吗?有没有更好的方法在运行时检查类型(重构不多)?

编辑:我还需要补充一点,这需要与 TI 编译器一起使用,目前仅支持 '03

【问题讨论】:

  • 在 Career Overflow 上获得一个帐户?
  • 我不知道if ( typeid(d) == typeid(Derived&lt;int&gt;*) ) 打算做什么。此外,您的任何测试似乎都没有副作用,它们都可能被优化器删除。
  • @KerrekSB 你的意思是 OP 在开始处理遗留代码之前应该辞职?
  • @πάνταῥεῖ:好吧,如果我被告知我必须快速制作 400k loc 的缓慢遗留代码,我想知道如果失败了我的选择......
  • @KerrekSB “好吧,如果我被告知我必须制作 400k loc ...” 我会尝试开发一些工具来支持重构(估计努力和成功机会分别);)...

标签: c++ casting rtti


【解决方案1】:

首先,请注意dynamic_cast 和 RTTI 之间存在很大差异:转换告诉您是否可以将基础对象视为 some 进一步派生的对象,但不一定是大多数派生对象。 RTTI 告诉您最精确的派生类型。自然前者更强大,也更昂贵。

那么,如果您有一个多态层次结构,那么您可以通过两种自然的方式来选择类型。它们是不同的;使用实际适用的那个。

void method1(Base * p)
{
    if (Derived * q = dynamic_cast<Derived *>(p))
    {
        // use q
    }
}

void method2(Base * p)
{
    if (typeid(*p) == typeid(Derived))
    {
        auto * q = static_cast<Derived *>(p);

        // use q
    }
}

另请注意,如果基类是虚拟基类,则方法 2 通常不可用。如果您的类不是多态的,则这两种方法都不适用。

在快速测试中,我发现方法 2 明显快于基于 ID 的手动解决方案,而后者又比动态转换解决方案(方法 1)快。

【讨论】:

  • 我主要说的是“绕过 RTTI”,因为在 TI 编译器和其他一些编译器上,仅启用 RTTI 会大大降低一切。完全关闭它会很好。我想我可能会实施一个易于更换的解决方案,这样我就可以使用不同的解决方案测试整个系统的速度。
  • 哦,没关系。我复制了示例 here 并使用 Base 和 Derived 更改了内容,忘记了您对虚拟需求的评论。
  • 一切似乎都在工作。有了这个和许多其他的变化,我已经能够将运行时间几乎减少一半!感谢您的帮助。
  • @CoryB:很高兴它很有用:-)
  • 演员表告诉您是否可以将基础对象视为进一步派生的对象您的意思不是指由base* 指向的(未知)对象吗?跨度>
【解决方案2】:

比较类的虚函数表怎么样?

快速而肮脏的概念证明:

void* instance_vtbl(void* c)
{
    return *(void**)c;
}

template<typename C>
void* class_vtbl()
{
    static C c;
    return instance_vtbl(&c);
}

// ...

begin = clock();
{
    for (size_t i = 0; i < 100000000; i++)
    {
        if (instance_vtbl(b) == class_vtbl<Derived<int>>())
            Derived <int>* d = static_cast<Derived<int>*> (b);
    }
}
end = clock();
elapsed = double(end - begin) / CLOCKS_PER_SEC;

cout << "Type elapsed: " << elapsed << endl;

使用 Visual C++ 的 /Ox 开关,这似乎比 type/getType 技巧快 3 倍。

【讨论】:

  • 这更快的原因是它不使用虚函数调用,我想。这些测试仍然没有副作用,不能用来比较不同的方法。这种方法不是一个可移植的解决方案(严格来说,可能是由于别名导致的未定义行为)。我猜它间接依赖于 RTTI,否则两个类的 vtable 可能是相同的(=> 可靠吗?)
  • 这对于任何不是默认可构造的类型以及作为 UB 的类型都有问题。 (对于没有 vtables 的类型也失败了,但是 meh)
【解决方案3】:

鉴于这种类型的代码

class A {
};

class B : public A {
}

A * a;
B * b = dynamic_cast<B*> (a);
if( b != 0 ) // do something B specific

修复它的多态(对吗?)方法是这样的

class A {
public:
    virtual void specific() { /* do nothing */ }
};

class B : public A {
public:
    virtual void specific() { /* do something B specific */ }
}

A * a;
if( a != 0 ) a->specific();

【讨论】:

    【解决方案4】:

    当 MSVC 2005 首次发布时,64 位代码的 dynamic_cast 比 32 位代码慢得多。我们想要一个快速简单的修复。这就是我们的代码的样子。它可能违反了各种良好的设计规则,但删除 dynamic_cast 的转换可以通过脚本自动完成。

    class dbbMsgEph {
    public:
        virtual dbbResultEph *              CastResultEph() { return 0; }
        virtual const dbbResultEph *        CastResultEph() const { return 0; }
    };
    
    class dbbResultEph : public dbbMsgEph {
    public:
        virtual dbbResultEph *              CastResultEph() { return this; }
        virtual const dbbResultEph *        CastResultEph() const { return this; }
        static dbbResultEph *               Cast( dbbMsgEph * );
        static const dbbResultEph *         Cast( const dbbMsgEph * );
    };
    
    dbbResultEph *
    dbbResultEph::Cast( dbbMsgEph * arg )
    {
        if( arg == 0 ) return 0;
        return arg->CastResultEph();
    }
    
    const dbbResultEph *
    dbbResultEph::Cast( const dbbMsgEph * arg )
    {
        if( arg == 0 ) return 0;
        return arg->CastResultEph();
    }
    

    我们曾经有过

    dbbMsgEph * pMsg;
    dbbResultEph * pResult = dynamic_cast<dbbResultEph *> (pMsg);
    

    我们把它改成

    dbbResultEph * pResult = dbbResultEph::Cast (pMsg);
    

    使用简单的 sed(1) 脚本。而且虚函数调用效率很高。

    【讨论】:

      【解决方案5】:

      //在发布模块(VS2008)中是这样的:

      cout << "Base Type: " << (b->getType() == Base::type()) << endl;
      

      我猜是因为优化。所以我改变了 Derived::type() 的实现

      template <typename T>
      struct Derived : public Base
      {
          static intptr_t type() 
          { 
              cout << "different type()" << endl;
              return reinterpret_cast<intptr_t>(&type); 
          }
          virtual intptr_t getType() { return type(); }
      };
      

      那就不一样了,如果用这种方法怎么处理???

      【讨论】:

      猜你喜欢
      • 2011-06-11
      • 2010-11-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 2017-05-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多