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)
设置%al
movzbl %al, %eax
ret
其中(如果您不阅读程序集)是负载并与常数进行比较。相比之下,dynamic_cast 的等价物是:
#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
设置%al
LBB0_2:
movzbl %al, %eax
popq %rdx
ret
这是更多的代码,但杀手是对 __dynamic_cast 的调用,然后它必须通过 RTTI 数据结构并做一个非常通用的、动态计算的遍历这些东西。这比加载和比较慢了几个数量级。
好的,好的,所以它变慢了,为什么这很重要?这很重要,因为 LLVM 做了很多类型检查。优化器的许多部分都是围绕代码中的模式匹配特定结构构建的,并对它们执行替换。例如,这里有一些匹配简单模式的代码(它已经知道 Op0/Op1 是整数减法运算的左侧和右侧):
// (X*2) - X -> X
if (match(Op0, m_Mul(m_Specific(Op1), m_ConstantInt<2>())))
return Op1;
匹配运算符和 m_* 是模板元程序,归结为一系列 isa/dyn_cast 调用,每个调用都必须进行类型检查。使用 dynamic_cast 进行这种细粒度的模式匹配将是残酷的,而且速度非常慢。
最后还有一点,就是表现力。 LLVM 使用的different 'rtti' operators 用于表达不同的东西:类型检查、dynamic_cast、强制(断言)强制转换、空处理等。C++ 的 dynamic_cast(本机)不提供任何这些功能。
最后,有两种方法来看待这种情况。不利的一面是,C++ RTTI 对许多人想要的东西(完全反射)的定义过于狭隘,而且对于像 LLVM 所做的那样简单的事情也太慢了。从积极的方面来说,C++ 语言足够强大,我们可以将这样的抽象定义为库代码,并选择不使用语言特性。关于 C++,我最喜欢的一件事是库的强大和优雅。在我最不喜欢的 C++ 特性中,RTTI 甚至不是很高 :)!
-克里斯