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

时间:2009-12-17 20:55:24

标签: c++ performance printf cout

执行某些测试后,我发现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;

这样可以吗?

更新:感谢所有宝贵的反馈。答案摘要:如果您想避免棘手的解决方案,只需简单地不要将endlcout一起使用,因为它会隐式刷新缓冲区。请改用"\n"。如果您生成大型输出,那会很有趣。

9 个答案:

答案 0 :(得分:69)

直接的答案是肯定的,没关系。

很多人都围绕着如何提高速度的各种想法,但似乎有很多分歧是最有效的。我决定编写一个快速测试程序,以至少了解哪些技术可以做什么。

#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)。

答案 1 :(得分:19)

向流发送std::endl会附加newline并刷新流。随后调用cout.flush()是多余的。如果在计时coutprintf时完成此操作,则您不会将苹果与苹果进行比较。

答案 2 :(得分:12)

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

答案 3 :(得分:12)

另请注意,C ++流已同步到C流 因此,它需要额外的工作才能保持同步。

另一件需要注意的事情是确保将相同数量的流冲洗掉。如果你在一个系统上连续冲洗流而不是另一个系统,那肯定会影响测试的速度。

在假设一个比另一个更快之前你应该:

  • 从C I / O取消同步C ++ I / O(请参阅sync_with_stdio())。
  • 确保冲洗量相当。

答案 4 :(得分:9)

您可以通过增加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混合,则缓冲区刷新会使缓冲区大小增加。

答案 5 :(得分:5)

您可以使用sync_with_stdio更快地使C ++ IO。

cout.sync_with_stdio(false);

应使用cout来提高输出效果。

答案 6 :(得分:3)

不要担心printfcout之间的效果。如果要获得性能,请将格式化输出与非格式化输出分开。

puts("Hello World\n")printf("%s", "Hellow World\n")快得多。 (主要是由于格式化开销)。一旦你从纯文本中分离出格式化文件,你可以做一些技巧:

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

要加速格式化输出,诀窍是对字符串执行所有格式化,然后使用字符串(或缓冲区)的块输出:

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);

为了进一步提高程序的性能,请减少输出量。输出的东西越少,程序就越快。副作用是您的可执行文件大小也会缩小。

答案 7 :(得分:1)

好吧,我想不出任何理由真正使用cout来说实话。拥有一个庞大的庞大模板来做一些如此简单的事情在每个文件中都是完全疯狂的。此外,它被设计为尽可能慢地打字并且在第百万次打字之后&lt;&lt;&lt;&lt;然后在两者之间键入值并得到一些类似的&gt; variableName&gt;&gt;&gt;事故我再也不想那样做了。

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

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

所以我输入的类似于 dout +“这比”+ debugIoType +“的”+ cPlusPlusMethod +“更理智。至少IMO”; DOUT ++;

但你可以随心所欲。有很多文件,令人惊讶的是,这也改善了编译时间。

此外,混合使用C和C ++并没有什么问题,它应该只是偶然地完成,如果你正在使用导致首先使用C的问题的东西,你可以安全地说,你担心的最少是麻烦来自混合C和C ++。

答案 8 :(得分:0)

我的C ++书籍(仅供参考)推荐使用混合C ++和C方法。我很确定C函数会在C ++预期/持有的状态上进行践踏。