为什么在什么都不做的情况下,C++比C慢?

21 浏览
0 Comments

为什么在什么都不做的情况下,C++比C慢?

在我的机器上,C++执行空操作的速度几乎比C慢4倍。当使用g++编译以下文件时,C++的速度比gcc慢4倍:

int main() {}

通过time sh -c 'for i in $(seq 0 1000) ; do ./a.out ; done',C版本的运行时间是0.515秒,而C++版本的运行时间是2秒。为什么会这样呢?

这个程序什么都没做,而且两个版本的反汇编程序基本相同,所以为什么C++版本大致慢4倍呢?

我猜测是因为C++库加载的时间更长,但是汇编代码非常相似,我想不出C++慢这么多的原因。

编辑:嗯,看起来我只部分反汇编了程序(使用objdump -d而不是objdump -D),导致我没有看到库的加载。现在看到反汇编输出,很明显C++版本需要更长时间来加载其库。我一开始所说的“略有不同”是指在movcall中使用的地址有所不同,而没有调用其他函数(如果我正确理解objdump的输出意义的话)。

所以主要问题已解决,但是对于一个什么都不做的程序来说,2秒的时间太长了。如果有帮助的话,我使用的是x86_64处理器,并且我用gcc empty.cg++ empty.cpp构建了可执行文件。我认为问题确实是我使用了HDD,因此加载libstd++需要更长时间。我还使用了-static标志进行构建,现在得到的结果非常相似(0.246秒和0.241秒)。C二进制文件与C++二进制文件完全相同(如果这不是一个diff的错误)。

0
0 Comments

为什么C++在什么都不做的情况下比C慢?

实际上,你根本没有进行任何比较,因为你的程序根本什么都没做。你得到的不是程序执行时间,在你的情况下是null,而是进程执行时间,即加载+运行+终止的时间,如果你的程序需要更多资源,这个时间会更长。

一些数据可以说明这个问题:

假设我们有emptycemptycpp,分别使用gcc-10.3和g++-10.3编译。

在我的系统上:

  • emptyc

    • 文件大小:13232
    • ELF动态部分:17个条目(1个共享库)
    • truss系统调用报告:48个系统调用,包括6个打开和11个mmap
  • emptycpp

    • 文件大小:13312
    • ELF动态部分:20个条目(4个共享库)
    • truss系统调用报告:88个系统调用,包括15个打开和29个mmap

结论

使用g++编译的程序需要比使用gcc编译的相同程序更多的资源。

生成88个系统调用的进程可能比生成48个系统调用的进程更慢。

解决方法:

为了避免C++在不做任何事情时比C慢,可以考虑以下几点:

1. 注意代码的优化,尽量减少不必要的资源消耗。

2. 使用更高效的编译器和编译选项,以提高代码的执行效率。

3. 避免不必要的库和依赖项,只使用必要的功能和库。

4. 对代码进行性能测试和剖析,找出可能导致程序变慢的瓶颈,并进行相应的优化。

通过以上的措施,可以减少C++程序在不做任何事情时的性能损失,提高程序的执行效率。

0
0 Comments

为什么C++在什么都不做的情况下比C慢?

C++标准库的初始启动比C运行时更重。

C++ ABI需要为基本语言特性初始化一些结构。互斥锁用于线程安全初始化数据,异常,线程本地存储,RTTI等。

这就是为什么使用C++编译器创建的空可执行文件启动速度比空C程序慢。同样,使用汇编语言创建的空程序会比使用具有完整功能的C运行时(CRT,GNU LIBC等)创建的程序启动速度快得多。

我认为OP的巨大开销并不来自C++库的初始化。在我的机器上,调用所有libstdc++函数的时间小于1微秒(<1%),而OP指出它的C++程序在1000次调用中需要2秒(即2毫秒,比这个大2000倍)!

如果你有一个快速的SSD,而OP有一个慢速的旋转硬盘呢?如果OP确保在计时之前清除了所有页面缓存,并且你正在重复运行一个具有热缓存的程序,保持所需的确切页面?

据我所知,C++库的初始化不会访问存储设备(只有链接器在这种情况下才会访问)。系统调用跟踪证明了这在libstdc++ 6.0.29中不是这种情况。此外,我的SSD的延迟明显大于1微秒。此外,OP明确提供了用于测量时间的命令(我使用相同的命令),并且没有进行显式的页面清除(为什么在glibc中会有所不同呢?)。最后,即使硬盘可能很慢,操作系统也应该将文件放入内存缓存中(特别是对于许多重复访问的情况)。

如果你认为这种情况不适合空白的情况,那么是的,没有链接到libstdc++,但仍然链接了libc,可能是libm和libgcc_s,所有这些都必须映射到进程的地址空间,并且可能从实际存储中加载。

据我所知,你展示的系统调用来自链接器而不是libstdc++的初始化。如果静态链接C++库,它们将从执行中删除(虽然初始化应该仍然存在)。这就是为什么我要求OP静态编译其代码的原因之一,也部分是我认为开销来自链接器的原因(请参阅上面的评论)。

那些读取应该命中磁盘缓存。我觉得这个解释是不充分的。

你可以尝试使用C++编写一些仅操作系统内核的小测试,你会看到初始化所需的C++ ABI有多少。当然,如果你不使用编译器选项关闭异常、线程安全的数据初始化和RTTI。

0