printf比std::cout快5倍以上?

15
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <ctime>

int main(int argc, char* argv[])
{
    std::clock_t start;
    double duration;    

    std::cout << "Starting std::cout test." << std::endl;
    start = std::clock();

    for (int i = 0; i < 1000; i++)
    {
        std::cout << "Hello, World! (" << i << ")" << std::endl;
    }

    duration = (std::clock() - start) / (double) CLOCKS_PER_SEC;

    std::cout << "Ending std::cout test." << std::endl;
    std::cout << "Time taken: " << duration << std::endl;

    std::system("pause");

    std::cout << "Starting std::printf test." << std::endl;
    start = std::clock();

    for (int i = 0; i < 1000; i++)
    {
        std::printf("Hello, World! (%i)\n", i);
        std::fflush(stdout);
    }

    duration = (std::clock() - start) / (double) CLOCKS_PER_SEC;

    std::cout << "Ending std::printf test." << std::endl;
    std::cout << "Time taken: " << duration << std::endl;

    system("pause");

    return 0;
}

现在,这里是前五次运行的时间:

  • std::cout测试:1.125秒; printf测试:0.195
  • std::cout测试:1.154秒; printf测试:0.230
  • std::cout测试:1.142秒; printf测试:0.216
  • std::cout测试:1.322秒; printf测试:0.221
  • std::cout测试:1.108秒; printf测试:0.232

正如您所看到的,使用printf再加上刷新操作fflush的时间约为使用std::cout的5倍少。

虽然我确实预计使用std::cout<<操作可能会略微慢一些(几乎可以忽略不计),但我没有料到会有如此巨大的差异。我做的测试公平吗?如果是这样,那么第一个测试比第二个测试慢这么多,如果它们本质上做了完全相同的事情,是什么原因呢?


2
他在printf版本中也进行了刷新,所以那不可能是原因吧? - Cubic
2
可能的重复问题并没有回答我的主要问题,“第一个测试为什么比第二个测试慢得多……”即为什么printf更快? - ApprenticeHacker
3
请注意,std::cout << "Hello, World! (" << i << ")" << std::endl; 包含 4 次函数调用,而 printf 只有一次。尝试像使用 cout 那样使用 printf 进行 5 次打印,并观察发生了什么。你会意识到性能损失中有多少是由于函数调用,以及库本身造成的影响。 - Shahbaz
2
你当前的计时是没有意义的,因为 std::cout 与 stdout 同步,并且会做很多“额外”的工作来维护同步。如果你将它们解耦,你会看到加速(虽然仍然较慢)std::cout.sync_with_stdio(false); - Martin York
1
每次使用 printf 刷新 stdout 是相当不公平的。没有 std::fflush(stdout);printf 循环执行得更快。虽然使用 << std::endl 是惯用法,但使用 fflush(stdout) 却不是惯用法。毫无戒心的 C++ 程序员很可能会比使用 stdout 更低效地使用 cout - chqrlie
显示剩余3条评论
6个回答

12

试一下这个:

#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <iostream>

int main(int argc, char* argv[])
{
#if defined(NOSYNC)
    std::cout.sync_with_stdio(false);
#endif

    std::cout << "Starting std::cout test." << std::endl;

    std::clock_t start = std::clock();

    for (int i = 0; i < 1000; i++)
    {   
        std::cout << "Hello, World! (" << i << ")" << std::endl;
    }   

    clock_t mid = std::clock();

    for (int i = 0; i < 1000; i++)
    {   
        std::printf("Hello, World! (%i)\n", i); 
        std::fflush(stdout);
    }   

    std::clock_t end = std::clock();

    std::cout << "Time taken: P1 " << ((mid-start)*1.0/CLOCKS_PER_SEC) << std::endl;

    std::cout << "Time taken: P2 " << ((end-mid)*1.0/CLOCKS_PER_SEC) << std::endl;


    return 0;
}

然后我得到:

> g++ -O3 t13.cpp
> ./a.out
# lots of lines deleted
Time taken: P1 0.002517
Time taken: P2 0.001872

> g++ -O3 t13.cpp -DNOSYNC   
> ./a.out
# lots of lines deleted
Time taken: P1 0.002398
Time taken: P2 0.001878

所以P2时间不会改变。
但是使用std::cout.sync_with_stdio(false);可以提高P1时间(即std::cout),因为代码不再尝试使两个流(std::cout和标准输出)同步。如果你只写纯C++并且仅使用std::cout,那就不是问题。


10

为了进行真正的苹果对比,重新编写你的测试,以便在测试用例之间唯一变化的是使用的打印函数:

int main(int argc, char* argv[])
{
    const char* teststring = "Test output string\n";
    std::clock_t start;
    double duration;

    std::cout << "Starting std::cout test." << std::endl;
    start = std::clock();

    for (int i = 0; i < 1000; i++)
        std::cout << teststring;
    /* Display timing results, code trimmed for brevity */

    for (int i = 0; i < 1000; i++) {
        std::printf(teststring);
        std::fflush(stdout);
    }
    /* Display timing results, code trimmed for brevity */
    return 0;
}

通过这种方式,你将仅测试printfcout函数调用之间的差异。你不会因为多个<<调用等而遭受任何差异。如果你尝试这样做,我猜你会得到一个非常不同的结果。


嗯,现在只有大约0.06秒的差距。看来这回答了我的问题。 - ApprenticeHacker
6
@bta,这个测试似乎不公平。对于printf部分,在每次迭代后都刷新了stdout,但std::cout的代码没有这样做。应该在std::cout部分添加<< std::flush()。 - oz10
1
添加以下内容:std::cout.sync_with_stdio(false); - Martin York
4
如果cout和printf之间的性能差异是由于将数字i格式化为字符串,则此比较无用。 - joydeepb
@joydeepb- 可能不是这样,但在这种情况下,我的测试显示多个<<运算符很可能是罪魁祸首,所以我没有费心展示测试其他事物的示例代码。然而,如果int-to-string转换是罪魁祸首,您可以应用相同的一般原则(消除变量以简化问题)来解决问题。 - bta
2
如果差异是由于需要比printf调用更多的<<调用,则完全可以以这种方式进行比较。简化测试到你所做的程度是不现实的。 - Mark Ransom

4

使用

cout << "\n";

为了防止缓冲,可以实现更快的速度。

2
大约10年前,Scott Meyers测试了iostreamscanf/printf的效率。取决于编译器和环境,有时候scanf/printf比iostream快20%,有时甚至快200%。但iostream从未比scanf/printf快过。根据另外两个给出的例子,scanf/printf仍然比iostream快。 然而,Meyers说:“在一个真正有用的程序中,不会有差别。” 根据Google的编程风格([http://google-styleguide.googlecode.com/svn/trunk/cppguide.html]),除了用于日志记录之外,不应使用流。 iostream的替代方法是自己封装scanf/printf。

0

我在我的笔记本电脑上尝试了这个测试,运行着Windows 10,WSL Ubuntu,CLion 2018和GCC。没有进行任何优化:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <ctime>

int main(int argc, char *argv[]) {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::clock_t start;
    double duration1, duration2;

    std::cout << "Starting std::cout test.\n";
    start = std::clock();

    for (int i = 0; i < 100000; i++) {
        std::cout << "Hello, World! (" << i << ")" << std::endl;
    }

    duration1 = (std::clock() - start) / (double) CLOCKS_PER_SEC;

    std::cout << "Starting std::printf test.\n";
    start = std::clock();

    for (int i = 0; i < 100000; i++) {
        std::printf("Hello, World! (%i)\n", i);
        std::fflush(stdout);
    }

    duration2 = (std::clock() - start) / (double) CLOCKS_PER_SEC;

    std::cout << "Time taken: cout " << duration1 << std::endl;
    std::cout << "Time taken Printf: " << duration2 << std::endl;

    return 0;
}

结果:

Test1: Cout: 2.25, Printf: 2.45312  (Cout run first)
Test2: Cout: 2.42188, Printf: 2.07812 (Printf Run first)
Test3: Cout: 2.26562, Printf: 2.25 (Cout run first)
Test4: Cout 2.46875, Printf: 2.57812 (Printf run first)

TL;DR:使用std::ios_base::sync_with_stdio(false)std::cin.tie(nullptr)将两个函数带到几乎相同的水平。

0
我只有一个计算机的编程范围,因此测试不多。无论如何,在我的构建中使用完整详细代码时,std::cout要快得多。我正在使用Cpp14。
我只是认为某些人对C ++有所偏好。是的,C是一种很棒的语言,但从逻辑上讲,我不明白printf怎么可能比std cout更快。Printf必须在运行时从void指针进行类型转换。而Cout是在编译时完成这个过程。

如果iostream在您的平台上确实更快,那么了解该平台将非常有用。 iostream比printf慢得多的原因是iostream使用大量函数调用,包括许多虚函数调用(iostream本身不做任何事情,它将所有内容转发到streambuf虚函数)。 如果您需要重定向流,则这非常有用,但如果您不使用它,则会浪费资源。 - Ben Voigt
好的,我不太清楚你指的是哪个平台,但我正在使用Win 10,Intel Celeron CPU G3900,4 GB内存,CPP 14。你应该记住,CPP每三年会进化一次。许多函数调用都会被编译掉。 - user9599745
编译器和C++标准库是平台非常重要的细节...CPU(你提到了它,还包含缓存信息)和RAM(你只提到了大小,但没有时钟速率、DDR代数、通道数量或延迟)在这种情况下不太重要,但有时会对性能和优化产生很大影响。 - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接