c++ - cpp文件 - c和cpp的区别



编译为C++与C时,GCC代码生成的巨大差异 (1)

额外的代码用于处理未对齐,因为使用的指令vmovdqa64需要64字节对齐。

我的测试显示,即使标准没有,gcc确实允许另一个模块中的定义在C模式下覆盖此处的定义。 该定义可能仅符合基本对齐要求(4个字节),因此编译器不能依赖更大的对齐。 从技术上讲,gcc为此暂定定义发出.comm程序集指令,而外部定义在.data部分中使用普通符号。 在链接期间,此符号优先于.comm

请注意,如果您将程序更改为使用extern unsigned int buffer[2048]; 那么即使C ++版本也会增加代码。 相反,使其成为static unsigned int buffer[2048]; 将C版本转换为优化版本。

我一直在玩x86-64程序集试图了解更多有关可用的各种SIMD扩展(MMX,SSE,AVX)。

为了了解GCC如何将不同的C或C ++构造转换为机器代码,我一直在使用Compiler Explorer ,这是一个极好的工具。

在我的一个“播放会话”期间,我想看看GCC如何优化整数数组的简单运行时初始化。 在这种情况下,我试图将数字0到2047写入2048个无符号整数的数组。

代码如下:

unsigned int buffer[2048];

void setup()
{
  for (unsigned int i = 0; i < 2048; ++i)
  {
    buffer[i] = i;
  }
}

如果我启用优化和AVX-512指令-O3 -mavx512f -mtune=intel GCC 6.3生成一些非常聪明的代码:)

setup():
        mov     eax, OFFSET FLAT:buffer
        mov     edx, OFFSET FLAT:buffer+8192
        vmovdqa64       zmm0, ZMMWORD PTR .LC0[rip]
        vmovdqa64       zmm1, ZMMWORD PTR .LC1[rip]
.L2:
        vmovdqa64       ZMMWORD PTR [rax], zmm0
        add     rax, 64
        cmp     rdx, rax
        vpaddd  zmm0, zmm0, zmm1
        jne     .L2
        ret
buffer:
        .zero   8192
.LC0:
        .long   0
        .long   1
        .long   2
        .long   3
        .long   4
        .long   5
        .long   6
        .long   7
        .long   8
        .long   9
        .long   10
        .long   11
        .long   12
        .long   13
        .long   14
        .long   15
.LC1:
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16
        .long   16

但是,当我测试通过添加标志-xc使用GCC C编译器编译相同代码时将生成的-xc我真的很惊讶。

我期望类似的,如果不相同的结果,但C编译器似乎生成更复杂,可能也更慢的机器代码。 生成的程序集太大,无法完全粘贴到此处,但可以通过以下链接在godbolt.org上查看。

生成的代码的片段,第58行到第83行,如下所示:

.L2:
        vpbroadcastd    zmm0, r8d
        lea     rsi, buffer[0+rcx*4]
        vmovdqa64       zmm1, ZMMWORD PTR .LC1[rip]
        vpaddd  zmm0, zmm0, ZMMWORD PTR .LC0[rip]
        xor     ecx, ecx
.L4:
        add     ecx, 1
        add     rsi, 64
        vmovdqa64       ZMMWORD PTR [rsi-64], zmm0
        cmp     ecx, edi
        vpaddd  zmm0, zmm0, zmm1
        jb      .L4
        sub     edx, r10d
        cmp     r9d, r10d
        lea     eax, [r8+r10]
        je      .L1
        mov     ecx, eax
        cmp     edx, 1
        mov     DWORD PTR buffer[0+rcx*4], eax
        lea     ecx, [rax+1]
        je      .L1
        mov     esi, ecx
        cmp     edx, 2
        mov     DWORD PTR buffer[0+rsi*4], ecx
        lea     ecx, [rax+2]

正如您所看到的,此代码具有许多复杂的移动和跳转,并且通常感觉像执行简单数组初始化的非常复杂的方式。

为什么生成的代码有这么大的差异?

与C编译器相比,GCC C ++编译器在优化C和C ++中有效的代码方面是否更好?





x86-64