为什么C ++的初始分配比C大得多?

时间:2019-06-20 18:43:16

标签: c++ c benchmarking

使用相同的代码时,只需更改编译器(从C编译器到C ++编译器)将更改分配的内存量。我不太确定为什么会这样,并且想进一步了解。到目前为止,我得到的最好的答复是“可能是I / O流”,它不是很具描述性,使我想知道C ++的“您不用为不使用的东西付钱”。 / p>

我正在使用分别为7.0.1-8和8.3.0-6的Clang和GCC编译器。我的系统在最新的Debian 10(Buster)上运行。基准测试是通过Valgrind Massif完成的。

#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

所使用的代码不会更改,但是无论我是以C还是C ++进行编译,它都会更改Valgrind基准测试的结果。但是,这些值在编译器之间保持一致。该程序的运行时分配(峰值)如下:

  • GCC(C):1,032字节(1 KB)
  • G ++(C ++):73,744字节(〜74 KB)
  • C声(C):1,032字节(1 KB)
  • Clang ++(C ++):73,744字节(〜74 KB)

对于编译,我使用以下命令:

clang -O3 -o c-clang ./main.c
gcc -O3 -o c-gcc ./main.c
clang++ -O3 -o cpp-clang ./main.cpp
g++ -O3 -o cpp-gcc ./main.cpp

对于Valgrind,我在每种编译器和语言上运行valgrind --tool=massif --massif-out-file=m_compiler_lang ./compiler-lang,然后在ms_print上显示峰。

我在这里做错什么了吗?

2 个答案:

答案 0 :(得分:149)

堆用法来自C ++标准库。它在启动时分配内存供内部库使用。如果您不反对它,则C和C ++版本之间的差应该为零。使用GCC和Clang,您可以使用以下命令编译文件:

g++ -Wl,--as-needed main.cpp

这将指示链接器不要链接未使用的库。在您的示例代码中,未使用C ++库,因此它不应链接到C ++标准库。

您也可以使用C文件进行测试。如果您使用:

gcc main.c -lstdc++

即使您已经构建了C程序,也会重新显示堆使用情况。

堆的使用显然取决于您正在使用的特定C ++库实现。您的情况就是GNU C ++库libstdc++。其他实现可能不会分配相同数量的内存,或者可能根本不分配任何内存(至少在启动时不分配)。例如,LLVM C ++库(libc++)在启动时不进行堆分配。至少在我的Linux机器上:

clang++ -stdlib=libc++ main.cpp

堆的使用与根本没有针对它的链接相同。

(如果编译失败,则可能未安装libc ++。程序包名称通常包含“ libc ++”或“ libcxx”。)

答案 1 :(得分:14)

GCC和Clang都不是编译器-它们实际上是工具链驱动程序。这意味着它们将调用编译器,汇编器和链接器。

如果使用C或C ++编译器编译代码,则将获得相同的程序集。汇编器将产生相同的对象。不同之处在于,工具链驱动程序将为两种不同的语言向链接器提供不同的输入:不同的启动程序(C ++需要代码来执行在名称空间级别具有静态或线程本地存储持续时间的对象的构造函数和析构函数,并且需要用于堆栈的基础结构框架以支持异常处理期间的展开,例如C ++标准库(在命名空间级别还具有静态存储持续时间的对象),以及可能的其他运行时库(例如具有堆栈展开基础结构的libgcc)。

简而言之,不是编译器引起了占用空间的增加,而是您通过选择C ++语言选择使用的东西的链接。

C ++确实具有“只为所用内容付费”的理念,但是通过使用该语言,您可以为此付费。您可以禁用部分语言(RTTI,异常处理),但是随后您将不再使用C ++。如另一个答案中所述,如果您根本不使用标准库,则可以指示驱动程序将其忽略(--Wl,-按需),但是如果您不打算使用任何功能,的C ++或其库,为什么还要选择C ++作为编程语言?