c++ - 什么会使 C++ RTTI 不受欢迎?

标签 c++ llvm rtti

查看 LLVM 文档,他们提到 they use "a custom form of RTTI" ,这就是他们拥有 isa<> 的原因, cast<>dyn_cast<>模板化函数。

通常,阅读一个库重新实现了一种语言的一些基本功能是一种可怕的代码气味,只是邀请运行。然而,这是我们正在谈论的 LLVM:这些人正在开发一个 C++ 编译器一个 C++ 运行时。如果他们不知道自己在做什么,那我就完蛋了,因为我更喜欢 clanggcc Mac OS 附带的版本。

不过,由于经验不如他们,我想知道普通 RTTI 的缺陷是什么。我知道它只适用于具有 v-table 的类型,但这只会引发两个问题:

  • 既然你只需要一个虚拟方法来拥有一个vtable,他们为什么不把一个方法标记为virtual ?虚拟析构函数似乎很擅长这一点。
  • 如果他们的解决方案不使用常规 RTTI,知道它是如何实现的吗?

最佳答案

LLVM 推出自己的 RTTI 系统有几个原因。该系统简单而强大,在 the LLVM Programmer's Manual 的一节中进行了描述。 .正如另一位海报所指出的,Coding Standards C++ RTTI 带来了两个主要问题:1) 空间成本和 2) 使用它的性能不佳。

RTTI 的空间成本相当高:每个带有 vtable(至少一个虚拟方法)的类都会获取 RTTI 信息,其中包括类的名称和有关其基类的信息。此信息用于实现 typeid运算符以及 dynamic_cast .因为这个成本是为每个带有 vtable 的类支付的(不,PGO 和链接时优化没有帮助,因为 vtable 指向 RTTI 信息)LLVM 使用 -fno-rtti 构建。根据经验,这节省了大约 5-10% 的可执行文件大小,这是相当可观的。 LLVM 不需要等效的 typeid,因此保留每个类的名称(以及 type_info 中的其他内容)只是浪费空间。

如果您进行一些基准测试或查看为简单操作生成的代码,很容易看出性能不佳。 LLVM isa<> 运算符通常编译为单个加载并与常量进行比较(尽管类根据它们实现其 classof 方法的方式来控制它)。这是一个简单的例子:

#include "llvm/Constants.h"
using namespace llvm;
bool isConstantInt(Value *V) { return isa<ConstantInt>(V); }

这编译为:

$ clang t.cc -S -o - -O3 -I$HOME/llvm/include -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -mkernel -fomit-frame-pointer
...
__Z13isConstantIntPN4llvm5ValueE:
    cmpb    $9, 8(%rdi)
    sete    %al
    movzbl  %al, %eax
    ret

which (if you don't read assembly) is a load and compare against a constant. In contrast, the equivalent with dynamic_cast is:

#include "llvm/Constants.h"
using namespace llvm;
bool isConstantInt(Value *V) { return dynamic_cast<ConstantInt*>(V) != 0; }

编译为:

clang t.cc -S -o - -O3 -I$HOME/llvm/include -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -mkernel -fomit-frame-pointer
...
__Z13isConstantIntPN4llvm5ValueE:
    pushq   %rax
    xorb    %al, %al
    testq   %rdi, %rdi
    je  LBB0_2
    xorl    %esi, %esi
    movq    $-1, %rcx
    xorl    %edx, %edx
    callq   ___dynamic_cast
    testq   %rax, %rax
    setne   %al
LBB0_2:
    movzbl  %al, %eax
    popq    %rdx
    ret

This is a lot more code, but the killer is the call to __dynamic_cast, which then has to grovel through the RTTI data structures and do a very general, dynamically computed walk through this stuff. This is several orders of magnitude slower than a load and compare.

Ok, ok, so it's slower, why does this matter? This matters because LLVM does a LOT of type checks. Many parts of the optimizers are built around pattern matching specific constructs in the code and performing substitutions on them. For example, here is some code for matching a simple pattern (which already knows that Op0/Op1 are the left and right hand side of an integer subtract operation):

  // (X*2) - X -> X
  if (match(Op0, m_Mul(m_Specific(Op1), m_ConstantInt<2>())))
    return Op1;

匹配运算符和 m_* 是模板元程序,归结为一系列 isa/dyn_cast 调用,每个调用都必须进行类型检查。使用 dynamic_cast 进行这种细粒度的模式匹配会很残酷,而且速度非常慢。

最后还有一点,就是表现力。 different 'rtti' operators LLVM 用于表达不同的东西:类型检查、dynamic_cast、强制(断言)强制转换、null 处理等。C++ 的 dynamic_cast 不( native )提供任何这些功能。

最后,有两种方法来看待这种情况。不利的一面是,C++ RTTI 对许多人想要的东西(完全反射)的定义过于狭隘,而且对于像 LLVM 所做的那样简单的事情也太慢了。从积极的方面来说,C++ 语言足够强大,我们可以将这样的抽象定义为库代码,并选择不使用语言特性。关于 C++,我最喜欢的一件事是库的强大和优雅。在我最不喜欢的 C++ 特性中,RTTI 甚至不是很高 :)!

-克里斯

关于c++ - 什么会使 C++ RTTI 不受欢迎?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5134975/

相关文章:

c++ - operator<<(ostream& os, ...) 用于模板类

c++ - 是否有任何可观察的 Qt 容器类?

c++ - 如何使用编译数据库为文件生成 LLVM 位码?

delphi - 如何在 Delphi XE 中使用 RTTI 设置记录值

delphi - 如何对对象执行 TRTTIProperty 的转换

c++ - C++将指向成员的指针作为模板参数传递

C++ : When is a declared variable is initialized without assigning it to any value?

c - 利用 LLVM 的半路交叉编译 - 在 Raspberry Pi 上编译更快

c++ - LLVM 存储操作数必须是指针