50

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

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

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

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

这样可以吗?

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

4

9 回答 9

74

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

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

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

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

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 << "\n";
}

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 << "\n";
}

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

void use_stringstream() { 
    std::stringstream temp;
    for (int i=0; i<count; i++)
        temp << string << "\n";
    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, "\n"), 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 上运行了它。一次运行的输出(输出重定向到磁盘文件)如下所示:

          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

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

  1. 写入 NUL 设备时 printf/puts 比 cout 快得多
    • 但是 cout 在写入真实文件时保持得很好
  2. 相当多的建议优化效果不大
    • 在我的测试中,fill_n 和其他任何东西一样快
  3. 到目前为止,最大的优化是避免 endl
  4. cout.write 给出了最快的时间(虽然可能不是很大

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

于 2009-12-18T05:18:07.843 回答
21

发送std::endl到流会附加 anewline并刷新流。随后的调用cout.flush()是多余的。如果这是在计时时完成的coutprintf那么你不是在比较苹果和苹果。

于 2009-12-17T21:05:03.183 回答
13

默认情况下,C 和 C++ 标准输出流是同步的,因此写入一个会导致另一个刷新,因此不需要显式刷新。

于 2009-12-17T21:09:48.870 回答
12

另外,请注意 C++ 流与 C 流同步。
因此,它需要做额外的工作来保持同步。

要注意的另一件事是确保您冲洗流的数量相等。如果您在一个系统而不是另一个系统上连续刷新流,这肯定会影响测试的速度。

在假设一个比另一个快之前,您应该:

  • 从 CI/O 取消同步 C++ I/O(请参阅 sync_with_stdio() )。
  • 确保冲洗量相当。
于 2009-12-17T21:14:25.567 回答
10

printf您可以通过增加以下的缓冲区大小来进一步提高性能stdout

setvbuf (stdout, NULL, _IOFBF, 32768);  // any value larger than 512 and also a
                  // a multiple of the system i/o buffer size is an improvement

调用操作系统执行 i/o 的次数几乎总是最昂贵的组件和性能限制因素。

当然,如果cout输出与 混合stdout,则缓冲区刷新会破坏增加缓冲区大小的目的。

于 2009-12-17T21:10:58.003 回答
5

你可以sync_with_stdio用来使 C++ IO 更快。

cout.sync_with_stdio(false);

应该提高你的输出性能cout

于 2009-12-17T21:08:07.930 回答
3

Don't worry about the performance between printf and cout. If you want to gain performance, separate formatted output from non-formatted output.

puts("Hello World\n") is much faster than printf("%s", "Hellow World\n"). (Primarily due to the formatting overhead). Once you have isolated the formatted from plain text, you can do tricks like:

const char hello[] = "Hello World\n";
cout.write(hello, sizeof(hello) - sizeof('\0'));

To speed up formatted output, the trick is to perform all formatting to a string, then use block output with the string (or buffer):

const unsigned int MAX_BUFFER_SIZE = 256;
char buffer[MAX_BUFFER_SIZE];
sprintf(buffer, "%d times is a charm.\n", 5);
unsigned int text_length = strlen(buffer) - sizeof('\0');
fwrite(buffer, 1, text_length, stdout);

To further improve your program's performance, reduce the quantity of output. The less stuff you output, the faster your program will be. A side effect will be that your executable size will shrink too.

于 2009-12-17T22:08:19.580 回答
1

好吧,老实说,我想不出任何实际使用 cout 的理由。拥有一个庞大而笨重的模板来做一些如此简单的事情,这将是完全疯狂的,并且会出现在每个文件中。此外,就像它被设计为尽可能慢地输入,并且在第 100 万次输入 <<<< 然后在两者之间输入值并意外得到类似 >variableName>>> 的内容之后,我再也不想这样做了.

更不用说如果你包含 std 命名空间,世界最终会崩溃,如果你不这样做,你的打字负担会变得更加荒谬。

但是我也不太喜欢 printf 。对我来说,解决方案是创建我自己的具体类,然后在其中调用任何必要的 io 东西。然后你可以以任何你想要的方式和你想要的任何实现,你想要的任何格式等拥有非常简单的 io(通常你希望浮点数始终是一种方式,例如,不要无缘无故地将它们格式化为 800 种方式,所以把每次通话的格式都是一个笑话)。

所以我输入的只是dout+“这比“+debugIoType+”的“+cPlusPlusMethod+”更理智。至少是IMO”;杜++;

但你可以拥有任何你想要的东西。对于大量文件,这也能大大缩短编译时间,这令人惊讶。

此外,混合 C 和 C++ 并没有错,应该明智地进行,如果您使用的东西首先会导致使用 C 出现问题,那么可以肯定地说,您最担心的是混合 C 和C++。

于 2009-12-18T06:31:44.840 回答
0

我的 C++ 书籍 FYI 建议不要混合 C++ 和 C iomethods。我很确定 C 函数会践踏 C++ 预期/持有的状态。

于 2009-12-17T21:08:04.147 回答