【问题标题】:Huge speed difference between identical c and c++ programs相同的 c 和 c++ 程序之间的巨大速度差异
【发布时间】:2019-12-28 16:55:02
【问题描述】:

我对 c 和 c++ 编程非常陌生,所以我从基础开始。我为 c 和 c++ 编写了相同的斐波那契循环程序来测试相对速度。我认为对于如此简单的事情它们几乎相同,但是 c++ 版本要慢 60 倍。他们所做的只是循环并打印前 14 个斐波那契数 10,000 次。这是c版本:

#include <stdio.h>

int main (){
    int c = 0;
    int x, y, z;

    while(c < 10000)
    {
        x = 0;
        y = 1;
        while(x < 255)
        {
            printf("%d\n", x);
            z = x + y;
            x = y;
            y = z;
        }
        c++;
    }
    return 0;
}

这里是 c++ 版本:

#include <iostream>
using namespace std;

int main()
{
    int c = 0, x = 0, y = 0, z = 0;
    while(c < 10000)
    {
        x = 0;
        y = 1;
        while(x < 255)
        {
            cout << x << endl;
            z = x + y;
            x = y;
            y = z;
        }
        c++;
    }
    return 0;
}

我都用记事本++编写并使用代码块附带的mingw中的g++编译它们:

g++ -o fibc.exe fib.c -s
g++ -o fibcpp.exe fib.cpp -s

可执行文件的大小非常不同:c 为 8.5KB,c++ 为 784KB!我用 powershell 来计时:

Measure-Command {start-process "C:\Path\fibcpp.exe" -RedirectStandardOutput "C:\Path\cpp.txt" -Wait}

生成的文件是相同的,但是 c 版本需要 1 秒,而 c++ 版本需要 60 秒! (实际上,为 c 程序放一个 100 万的循环仍然只用了 13 秒)。我还在 Visual Studio 17 中编写了 c++,并在其中使用 x86 发布配置对其进行了编译。程序大小现在为 9.5KB,但运行时间与 g++ 版本相同:62 秒。为什么这么简单的程序会出现这种情况?

【问题讨论】:

  • 在 C++ 代码中尽量使用与 C 代码相同的标头。删除using namespace std; 并使用printf 而不是cout 并再次检查它们之间的区别。当你运行这样一个实验时,你应该尽可能地使用最接近的代码。
  • 尝试使用'\n'而不是std::endl作为初学者,你不需要每次都刷新。您是否尝试过使用优化进行编译,例如-O2?
  • 您不是在比较等效代码。默认情况下,C++ 流(std::cout 等)与其 C 等效项同步 - 这意味着默认情况下会影响性能。在执行输出之前尝试执行std::ios::sync_with_stdio(false) - 那么C++ 版本将更接近(尽管不完全等同)C 版本。此外,endl 流操作符会刷新流缓冲区,而在将'\n' 提供给printf() 时不会这样做。此外,基准测试通常对未优化的代码毫无意义。
  • 你能把 cout 换成 printf 吗?
  • 好吧,你们在 2 分钟内解决了我的问题 - 谢谢。在 c++ 代码中切换到 stdio 和 printf 就是一切——新的 exe 与 c 版本的大小和性能相同。如您所见,我对此非常陌生。

标签: c++ c performance


【解决方案1】:

您正在对 printfcout 进行基准测试,因为这些是您程序中的主要瓶颈。

printf 是一个非常慢的函数,但可能仍比 osteam 快,后者必须维护自己的模板元编程的私有地狱,以保持其与 C++ 标准所要求的一样灵活。 cout 绝对比 printf 灵活和复杂得多。更多功能意味着更慢的代码。

如果您真的想比较这两种语言,请删除打印函数并将其替换为具有外部链接的虚拟函数“foo”:

void foo (int x);
...

while(x < 255)
{
  foo(x);

当使用 gcc -O3 for x86 对该代码进行基准测试时,我得到的 C versionC++ version 的代码几乎相同。

唯一显着的区别是 C++ 在 main() 末尾添加的“CRT”粘胶。它正在调用 atexit 和 C 也会执行的各种其他清理,但可能在应用程序代码之外。在 C++ 中仍然会有开销,因为它必须调用具有静态存储持续时间的对象的构造和清理析构函数。 C 不必这样做。

【讨论】:

  • 如果不包括 iostream,那么两者实际上是相同的(除了 foo 的名称修改)。关键的一点是,仅仅包括 iostream 会为您的程序贡献一些(小)开销。
  • @MilesBudnek 我宁愿怀疑包含静态存储持续时间对象的任何内容都会产生开销。
  • 模板元编程不影响执行速度。诸如缓冲策略、刷新和同步之类的事情。
  • @aschepler 各种开销代码都会影响执行速度。可以在编译时解决的事情不会,但元编程几乎总是意味着开销代码。所以ostream 非常慢。
【解决方案2】:

C++cout慢的原因在here中有解释。

默认情况下,iostream 对象和 cstdio 流是同步的(就好像使用 true 作为参数调用此函数一样)。

所以默认情况下cout 与 stdio 同步。

尝试执行以下操作以加快速度。

ios_base::sync_with_stdio(false)

但要小心,这段代码会打印出你意想不到的东西。

#include <iostream>
#include <cstdio>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cout << "a\n";
    std::printf("b\n");
    std::cout << "c\n";
}

【讨论】:

    【解决方案3】:

    编译

    #include <iostream>
    using namespace std;
    
    int main()
    {
        ios_base::sync_with_stdio(false);
        int c = 0, x = 0, y = 0, z = 0;
        while(c < 10000)
        {
            x = 0;
            y = 1;
            while(x < 255)
            {
                cout << x << '\n';
                z = x + y;
                x = y;
                y = z;
            }
            c++;
        }
        return 0;
    }
    

    通过基本的现代优化 (-Os) 获得

    $ ls -l bin/c?
    -rwxr-xr-x 1 jthill jthill 16592 Feb 29 12:47 bin/c1
    -rwxr-xr-x 1 jthill jthill 17176 Feb 29 12:53 bin/c2
    -rwxr-xr-x 1 jthill jthill 17088 Feb 29 12:49 bin/c3
    $ 
    

    (你的C版本是c1,你的C++版本是c2,上面的C++版本是c3)。

    平均每次运行 100 次的时间,

    $ for x in {1..100}; do time bin/c1; done 2>&1 >$scratch | awk '/user/{++n; sub(/0m/,"",$2); tot+=$2}END{print tot/n}'
    0.00862
    $ for x in {1..100}; do time bin/c2; done 2>&1 >$scratch | awk '/user/{++n; sub(/0m/,"",$2); tot+=$2}END{print tot/n}'
    0.0428
    $ for x in {1..100}; do time bin/c3; done 2>&1 >$scratch | awk '/user/{++n; sub(/0m/,"",$2); tot+=$2}END{print tot/n}'
    0.00743
    

    如果使用得当,C++ 的 iostream 比 C 的 stdio

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-23
      • 1970-01-01
      • 1970-01-01
      • 2014-07-06
      • 1970-01-01
      • 2016-03-11
      相关资源
      最近更新 更多