混合 cout 和 printf 以获得更快的输出

2021-12-08 00:00:00 performance printf c++ cout

在执行一些测试后,我注意到 printfcout 快得多.我知道它依赖于实现,但在我的 Linux 机器上 printf 快 8 倍.所以我的想法是混合两种打印方法:我想使用 cout 进行简单的打印,我计划使用 printf 来产生巨大的输出(通常在循环中).我认为只要在切换到其他方法之前不要忘记刷新就可以安全:

After performing some tests I noticed that printf is much faster than cout. I know that it's implementation dependent, but on my Linux box printf is 8x faster. So my idea is to mix the two printing methods: I want to use cout for simple prints, and I plan to use printf for producing huge outputs (typically in a loop). I think it's safe to do as long as I don't forget to flush before switching to the other method:

cout << "Hello" << endl;
cout.flush();

for (int i=0; i<1000000; ++i) {
    printf("World!
");
}
fflush(stdout);

cout << "last line" << endl;
cout << flush;

这样可以吗?

更新:感谢所有宝贵的反馈.答案摘要:如果您想避免棘手的解决方案,只需坚持使用 cout 但不要使用 endl 因为它会隐式刷新缓冲区(减慢进程).使用 " " 代替.如果你产生大输出,这会很有趣.

Update: Thanks for all the precious feedbacks. Summary of the answers: if you want to avoid tricky solutions, simply stick with cout but don't use endl since it flushes the buffer implicitly (slowing the process down). Use " " instead. It can be interesting if you produce large outputs.

推荐答案

直接的回答是,是的,没关系.

The direct answer is that yes, that's okay.

很多人就如何提高速度提出了各种想法,但似乎对哪种方法最有效存在相当多的分歧.我决定编写一个快速测试程序,以至少了解哪些技术做了什么.

A lot of people have thrown around various ideas of how to improve speed, but there seems to be quite a bit of disagreement over which is most effective. I decided to write a quick test program to get at least some idea of which techniques did what.

#include <iostream>
#include <string>
#include <sstream>
#include <time.h>
#include <iomanip>
#include <algorithm>
#include <iterator>
#include <stdio.h>

char fmt[] = "%s
";
static const int count = 3000000;
static char const *const string = "This is a string.";
static std::string s = std::string(string) + "
";

void show_time(void (*f)(), char const *caption) { 
    clock_t start = clock();
    f();
    clock_t ticks = clock()-start;
    std::cerr << std::setw(30) << caption 
        << ": " 
        << (double)ticks/CLOCKS_PER_SEC << "
";
}

void use_printf() {
    for (int i=0; i<count; i++)
        printf(fmt, string);
}

void use_puts() {
    for (int i=0; i<count; i++) 
        puts(string);        
}

void use_cout() { 
    for (int i=0; i<count; i++)
        std::cout << string << "
";
}

void use_cout_unsync() { 
    std::cout.sync_with_stdio(false);
    for (int i=0; i<count; i++)
        std::cout << string << "
";
    std::cout.sync_with_stdio(true);
}

void use_stringstream() { 
    std::stringstream temp;
    for (int i=0; i<count; i++)
        temp << string << "
";
    std::cout << temp.str();
}

void use_endl() { 
    for (int i=0; i<count; i++)
        std::cout << string << std::endl;
}

void use_fill_n() { 
    std::fill_n(std::ostream_iterator<char const *>(std::cout, "
"), count, string);
}

void use_write() {
    for (int i = 0; i < count; i++)
        std::cout.write(s.data(), s.size());
}

int main() { 
    show_time(use_printf, "Time using printf");
    show_time(use_puts, "Time using puts");
    show_time(use_cout, "Time using cout (synced)");
    show_time(use_cout_unsync, "Time using cout (un-synced)");
    show_time(use_stringstream, "Time using stringstream");
    show_time(use_endl, "Time using endl");
    show_time(use_fill_n, "Time using fill_n");
    show_time(use_write, "Time using write");
    return 0;
}

在使用 VC++ 2013(x86 和 x64 版本)编译后,我在 Windows 上运行了它.一次运行的输出(输出重定向到磁盘文件)如下所示:

I ran this on Windows after compiling with VC++ 2013 (both x86 and x64 versions). Output from one run (with output redirected to a disk file) looked like this:

          Time using printf: 0.953
            Time using puts: 0.567
   Time using cout (synced): 0.736
Time using cout (un-synced): 0.714
    Time using stringstream: 0.725
            Time using endl: 20.097
          Time using fill_n: 0.749
           Time using write: 0.499

正如预期的那样,结果各不相同,但我觉得有几点很有趣:

As expected, results vary, but there are a few points I found interesting:

  1. 在写入 NUL 设备时,printf/puts 比 cout 快得多
    • 但是 cout 在写入真实文件时保持得很好
  • 在我的测试中,fill_n 与其他任何东西一样快

我最近编辑了代码以强制调用printf.Anders Kaseorg 很友好地指出――g++ 识别特定序列 printf("%s ", foo); 等价于 puts(foo);,并相应地生成代码(即生成代码来调用 puts 而不是 printf).将格式字符串移动到全局数组,并将其作为格式字符串传递会产生相同的输出,但强制它通过 printf 而不是 puts 产生.当然,他们也有可能在某一天对此进行优化,但至少现在 (g++ 5.1) 使用 g++ -O3 -S 进行的测试确认它实际上正在调用 printf(前面的代码编译为对 puts 的调用).

I've recently edited the code to force a call to printf. Anders Kaseorg was kind enough to point out--that g++ recognizes the specific sequence printf("%s ", foo); is equivalent to puts(foo);, and generates code accordingly (i.e., generates code to call puts instead of printf). Moving the format string to a global array, and passing that as the format string produces identical output, but forces it to be produced via printf instead of puts. Of course, it's possible they might optimize around this some day as well, but at least for now (g++ 5.1) a test with g++ -O3 -S confirms that it's actually calling printf (where the previous code compiled to a call to puts).

相关文章