为什么g ++和clang ++之间的编译时间差异很大?

时间:2014-10-23 05:16:28

标签: c++ gcc clang

我遇到了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?

1 个答案:

答案 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),其他用户肯定比我更有资格回答这个问题。