关于c ++:混合cout和printf以获得更快的输出

mixing cout and printf for faster output

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

1
2
3
4
5
6
7
8
9
10
11
cout <<"Hello" << endl;
cout.flush();

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

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

这样可以吗?

更新:感谢所有宝贵的反馈。答案总结:如果你想避免棘手的解决方案,就不要使用endlcout,因为它会隐式地刷新缓冲区。用"
"
代替。如果你能产生大量的产出,这会很有趣。


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <iostream>
#include <string>
#include <sstream>
#include <time.h>
#include <iomanip>
#include
#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上运行了这个程序。一次运行的输出(将输出重定向到磁盘文件)如下所示:

1
2
3
4
5
6
7
8
          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

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

  • 在写入nul设备时,printf/puts比cout快得多。
    • 但是在写真正的文件时不能很好地保持
  • 相当多的建议优化效果很小
    • 在我的测试中,填充速度和其他任何东西一样快
  • 到目前为止,最大的优化是避免ENDL
  • cout.write给出了最快的时间(尽管可能不会有很大的差距)
  • 我最近编辑了代码来强制调用printf。Anders Kaseog非常客气地指出,g++识别出特定的序列printf("%s
    ", foo);
    相当于puts(foo);并相应地生成代码(即生成代码来调用puts而不是printf)。将格式字符串移动到全局数组,并将其作为格式字符串传递,以生成相同的输出,但强制通过printf而不是puts生成。当然,他们也有可能在这一天进行优化,但至少目前(G++5.1)使用g++ -O3 -S的测试证实它实际上是在调用printf(之前的代码编译为调用puts的代码)。


    发送std::endl到流,附加一个newline并刷新流。随后调用cout.flush()是多余的。如果这是在coutprintf的时间选择上完成的,那么你就没有将苹果与苹果进行比较。


    另外,注意C++流是同步到C流的。因此,保持同步需要额外的工作。

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

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

    • 来自C I/O的非同步C++I/O(参见SycCyOffStdio-())。
    • 确保冲洗量是可比的。


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


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

    1
    2
    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混合,缓冲区刷新会破坏增加缓冲区大小的目的。


    你可以使用EDCOX1 5来更快地使C++ IO。

    1
    cout.sync_with_stdio(false);

    应该用cout提高你的输出性能。


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

    puts("Hello World
    ")
    printf("%s","Hellow World
    ")
    快得多。(主要是由于格式化开销)。一旦将格式化后的文本与纯文本隔离开来,您就可以执行以下操作:

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

    为了加速格式化输出,技巧是对字符串执行所有格式化,然后将块输出与字符串(或缓冲区)一起使用:

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

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


    好吧,老实说,我想不出任何实际使用cout的理由。拥有一个庞大的模板来做一些如此简单的事情,将在每个文件中,这是完全疯狂的。另外,它的设计是尽可能慢地输入,并且在第一百万次输入<<<之后,在中间输入值,然后在意外事件中获得诸如>variablename>>之类的东西,我再也不想这样做了。

    更不用说,如果您包含std名称空间,世界最终会内爆,如果您不包含,您的输入负担会变得更加荒谬。

    不过,我也不太喜欢printf。对于我来说,解决方案是创建自己的具体类,然后调用其中所需的任何IO内容。然后,您可以以任何方式使用真正简单的IO,也可以使用您想要的任何实现、您想要的任何格式等(通常,您希望浮动始终是一种方式,例如,不要无缘无故地以800种方式对其进行格式化,所以每次调用都要进行格式化是一个笑话)。

    所以我键入的是dout+"这比"+debugiotype+"的"+cplusplusMethod+"更理智。"IMO"至少";DOT++;

    但你可以随心所欲。有了大量的文件,这也大大提高了编译时间。

    此外,C和C++的混合并没有错,它应该公正地进行,如果你使用的是导致使用C的问题的东西,那么安全地说,至少你担心的是混合C和C++的麻烦。


    我的C++书籍推荐FC++和C语言混合,FYI。我很确定C函数是在C++所期望的状态下进行的。