【问题标题】:Strange GCC Behaviour奇怪的 GCC 行为
【发布时间】:2011-12-01 23:26:09
【问题描述】:

给定以下 C++ 代码:

struct vertex_type {
    float x, y, z;

    //vertex_type() {}
    //vertex_type(float x, float y, float z) : x(x), y(y), z(z) {}
};

typedef struct {
    vertex_type vertex[10000];
} obj_type;

obj_type cube = {
    {
        {-1, -1, -1},
        {1, -1, -1},
        {-1, 1, -1},
        {1, 1, -1},

        {-1, -1, 1},
        {1, -1, 1},
        {-1, 1, 1},
        {1, 1, 1}
    }
};

int main() {
    return 0;
}

当我将(当前已注释掉的)构造函数添加到 vertex_type 结构中时,编译时间突然增加了 10-15 秒。 被难住了,我查看了 gcc 生成的程序集(使用-S),发现代码生成的大小比以前大了几百倍。

...
movl    $0x3f800000, cube+84(%rip)
movl    $0x3f800000, cube+88(%rip)
movl    $0x3f800000, cube+92(%rip)
movl    $0x00000000, cube+96(%rip)
...
movl    $0x00000000, cube+119996(%rip)
...

通过省略构造函数定义,生成的程序集完全不同。

.globl cube
    .data
    .align 32
    .type   cube, @object
    .size   cube, 120
cube:
    .long   3212836864
    .long   3212836864
    .long   3212836864
    .long   1065353216
    .long   3212836864
    .long   3212836864
    .long   3212836864
    .long   1065353216
    .long   3212836864
    .long   1065353216
    .long   1065353216
    .long   3212836864
    .long   3212836864
    .long   3212836864
    .long   1065353216
    .long   1065353216
    .long   3212836864
    .long   1065353216
    .long   3212836864
    .long   1065353216
    .long   1065353216
    .long   1065353216
    .long   1065353216
    .long   1065353216
    .zero   24
    .text

显然编译器生成的代码有很大的不同。 这是为什么? 另外,为什么 gcc 在一种情况下将所有元素归零而不是另一种情况?

编辑: 我正在使用以下编译器标志:-std=c++0x with g++ 4.5.2.

【问题讨论】:

  • 这是一个疯狂的猜测,但试试这个:vertex_type() : x(), y(), z() {}
  • @AlfP.Steinbach 这正是我文件中的代码。没有别的了。你们有没有检查过代码?它有 {}。它是内联的...
  • @Alf 有问题的构造函数被注释掉了。
  • @Daniel:虽然 C++11 是官方的,但我认为你应该提到你的代码是故意特定于 C++11 的——例如它不会与 MSVC 一起编译。被视为 C++98/03 的代码(充其量)是不完整的。我相应地更改了标签。

标签: c++ gcc compiler-construction g++ c++11


【解决方案1】:

这是一个长期存在的missing optimization in GCC。它应该能够为这两种情况生成相同的代码,但它不能。

没有构造函数,您的 vertex_type 是一个 POD 结构,GCC 可以在编译时初始化静态/全局实例。使用构造函数,它所能做的最好的事情就是在程序启动时生成代码来初始化全局。

【讨论】:

  • 感谢您的洞察力和链接,但为什么它在一种情况下将剩余元素归零而不是另一种情况?
  • 这个答案不正确。带有构造函数的代码甚至不应该编译。看我的回答。
  • @Daniel 这可能是一个(不相关的)错误。我的 g++ 4.6.1 副本在这两种情况下都将其余元素归零(并且 not 还会生成 9976 个 movl 指令来填充零 - 它使用 .bss 隐式执行)。我手头没有 gcc 4.5。
  • @Zack,有趣的是,不使用 C++0x 处理 POD 案例确实将剩余值归零。
  • @Daniel:有趣的是,在 C++0x 中,Zack 提到的错误报告提供了解决方法 --> 将构造函数标记为 constexpr
【解决方案2】:

如果您有自定义构造函数,编译器应该为它创建的所有向量调用它。如果您不自己编写,则默认为生成的构造函数。但是由于没有类型是复杂的,所以它不需要调用它。并且该数组作为常量表存储在二进制文件中。

尝试内联你的默认构造函数并让它为空。当然,它可能只在启用优化的情况下工作

【讨论】:

  • 据我所知,不可能内联默认构造函数。他发布的构造函数代码是隐式内联的。
  • 可以在 MSVC 中使用 __forceinline 等编译器扩展
猜你喜欢
  • 2014-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-18
  • 1970-01-01
相关资源
最近更新 更多