我遇到了talk slide,第12页有一个例子说明了存在继承时类型检查的难度。
struct a {
typedef int foo;
};
struct a1 : a {};
struct a2 : a {};
#define X(b, a) \
struct a##1 : b##1, b##2 {}; \
struct a##2 : b##1, b##2 {};
X(a, b) X(b, c) X(c, d) X(d, e) X(e, f) X(f, g) X(g, h) X(h, i) X(i, j) X(j, k)
X(k, l) X(l, m) X(m, n) n1::foo main() {}
预处理后,它转换为:
struct a {
typedef int foo;
};
struct a1 : a {};
struct a2 : a {};
struct b1 : a1, a2 {};
struct b2 : a1, a2 {};
struct c1 : b1, b2 {};
struct c2 : b1, b2 {};
struct d1 : c1, c2 {};
struct d2 : c1, c2 {};
struct e1 : d1, d2 {};
struct e2 : d1, d2 {};
struct f1 : e1, e2 {};
struct f2 : e1, e2 {};
struct g1 : f1, f2 {};
struct g2 : f1, f2 {};
struct h1 : g1, g2 {};
struct h2 : g1, g2 {};
struct i1 : h1, h2 {};
struct i2 : h1, h2 {};
struct j1 : i1, i2 {};
struct j2 : i1, i2 {};
struct k1 : j1, j2 {};
struct k2 : j1, j2 {};
struct l1 : k1, k2 {};
struct l2 : k1, k2 {};
struct m1 : l1, l2 {};
struct m2 : l1, l2 {};
struct n1 : m1, m2 {};
struct n2 : m1, m2 {};
n1::foo main() {}
当我使用g ++(Debian 4.9.1-16)编译它时,它确实消耗了大量时间:
$ time g++ type_check.cc
real 29.35s
user 29.34s
sys 0.00s
虽然clang ++(clang版本3.4.2(标签/ RELEASE_34 / dot2-final),x86_64-unknown-linux-gnu)的速度要快得多。
$ time clang++ type_check.cc
real 0.06s
user 0.04s
sys 0.00s
为什么检查多重继承两侧的foo类型是否匹配对gcc / icc花费这么多时间,而不是为了clang?
答案 0 :(得分:5)
我想这是因为过早的优化而且是一个错误。
我认为GCC跟踪继承并且像Clang memoize基类一样,在解析“下一个基类”时立即发现歧义,但与Clang GCC不同,如果没有成员则跳过这个fase(但是它应该认为typedef是成员在我看来)
证据:
改变结构a
struct a {
int a; //add this
typedef int foo;
};
并且代码编译速度最快(至少相同的数量级)Clang。
在GCC 4.8.0 / 4.8.1 / 4.9.0
上测试编辑:
因为@HongxuChen现在正在编辑我要编辑的更多信息:这些都是个人考虑因素,可能也不正确,但对我来说似乎是合理的。
我并不感到惊讶,编译时间如此深入的继承树爆炸而没有记忆。
如果你尝试添加
,基本上你有指数级的复杂性X(a, b) X(b, c) X(c, d) X(d, e) X(e, f) X(f, g) X(g, h) X(h, i) X(i, j) X(j, k)
X(k, l) X(l, m) X(m, n)
X(n, o) //add this one
o1::foo main() {}
你会发现编译时间现在是两次。
每次编译器执行两次检查时,它的增长速度并不快,特别是考虑到内部编译器执行的复杂操作可能需要数千甚至更多,汇编指令和缓存未命中。
我们在你的情况下有2 ^ 14次操作(两次2 ^ 13)。假设2Ghz单核意味着 16.384次超过39秒的操作,意味着420次操作/秒,因此有470万次指令/操作。
似乎有470万条指令/操作是如此之多,但并非如此。将编译时间增加几秒钟非常容易..
强迫一个场景,Clang和GCC都不能使用memoization并看到哪个更快,但我不知道如何做到这一点,无论如何他们应该使用memoization来保存大部分工作。< / p>
注意: 我知道我没有直接调查GCC代码,这对GCC开发人员来说也许是一项繁重的任务。我有编写解析器和元编译器的最小经验,因此我部分地面对编译期间可能出现的最常见问题(不是直接GCC),其他用户肯定比我更有资格回答这个问题。