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

Jab*_*bba 48 c++ performance printf cout

在进行了一些测试后,我发现它printf比它快得多cout.我知道它依赖于实现,但在我的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;
Run Code Online (Sandbox Code Playgroud)

这样好吗?

更新:感谢所有宝贵的反馈.答案摘要:如果你想避免棘手的解决方案,只需简单地不使用endl,cout因为它会隐式刷新缓冲区.请"\n"改用.如果你产生大量输出会很有趣.

Jer*_*fin 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;
}
Run Code Online (Sandbox Code Playgroud)

在使用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
Run Code Online (Sandbox Code Playgroud)

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

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

  • 谢谢你的彻底测试.我一直以为`endl`只是``\n"`的别名.正如我所看到的,`endl`也调用了一个flush,而使用``\n"`将会执行缓冲,就像printf一样. (4认同)

小智 19

发送std::endl到流附加a newline并刷新流.随后的调用cout.flush()是多余的.如果这是在时间cout与时间printf比较完成时你没有比较苹果和苹果.

  • 回答我之前的评论:使用`"\n"`而不是`endl`来避免每行后的刷新.另请参见http://stackoverflow.com/questions/1924530/mixing-cout-and-printf-for-faster-output/1926432#1926432. (5认同)

小智 12

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


Mar*_*ork 12

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

另外需要注意的是确保冲洗流量相等.如果你在一个系统上连续冲洗流而不是另一个系统,那肯定会影响测试的速度.

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

  • 从CI/O取消同步C++ I/O(参见sync_with_stdio()).
  • 确保冲洗量相当.

  • 是的,这是不正确的。重新措词使其可口。 (2认同)

wal*_*lyk 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
Run Code Online (Sandbox Code Playgroud)

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

当然,如果cout输出混合stdout,则缓冲区刷新失败的目的是增加缓冲区大小.


Jua*_*uan 5

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

cout.sync_with_stdio(false);
Run Code Online (Sandbox Code Playgroud)

应该提高您的输出性能cout