std:cin真的很慢

std::cin really slow

本文关键字:真的 cin std      更新时间:2023-10-16

所以我试图为自己编写一个linux管道的命令。可以将其视为gnu"cat"或"sed"的副本,它从stdin获取输入,进行一些处理并写入stdout。

我最初写了一个AWK脚本,但想要更高的性能,所以我使用了以下c++代码:

std::string crtLine;
crtLine.reserve(1000);
while (true)
{
    std::getline(std::cin, crtLine);
    if (!std::cin) // failbit (EOF immediately found) or badbit (I/O error)
        break;
    std::cout << crtLine << "n";
}

这正是cat(没有任何参数)所做的。事实证明,这个程序和它的awk程序一样慢,远不如cat程序快。

在1GB文件上测试:

$time cat 'file' | cat | wc -l
real    0m0.771s
$time cat 'file' | filter-range.sh | wc -l
real    0m44.267s

我没有尝试getline(istream,string),而是尝试了cin.getline(buffer,size),但没有任何改进。这很尴尬,是缓冲问题吗?我还试着一次获取100KB,而不是一行,没有帮助!有什么想法吗?

编辑:你们说的有道理,但罪魁祸首不是字符串构建/复制,也不是扫描换行符。(缓冲区的大小也是如此)。看看这两个程序:

char buf[200];
while (fgets(buf, 200, stdin))
    std::cout << buf;
$time cat 'file' | ./FilterRange > /dev/null
real    0m3.276s


char buf[200];
while (std::cin.getline(buf, 200))
    std::cout << buf << "n";
$time cat 'file' | ./FilterRange > /dev/null
real    0m55.031s

它们都不操作字符串,都进行换行扫描,但其中一个比另一个慢17倍。它们的区别仅在于cin的使用。我认为我们可以放心地得出结论,cin搞砸了时间。

要为标准I/O流对象获得良好性能,首先要做的事情是关闭与标准C流对象的同步:

std::ios_base::sync_with_stdio(false);

一旦你做到了这一点,你应该会得到更好的表现。不过,你是否能取得好成绩是另一个问题。

由于有些人声称cat在内部会做什么很有趣,以下是将一个流复制到另一个流的最快方法:

std::cout << std::cin.rdbuf();

如果你能正确地将D_2从一个流std::copy()到另一个流,我会很高兴,但这对大多数I/O流实现来说都不太好:

std::copy(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>(),
          std::ostreambuf_iterator<char>(std::cout));

我希望我最终能做到最好。。。

这正是cat(没有任何参数)所做的。

不是。这与/bin/cat具有完全相同的效果,但它不使用相同的方法。

/bin/cat看起来更像这样:

while( (readSize = read(inFd, buffer, sizeof buffer)) > 0)
  write(outFd, buffer, readSize);

请注意,/bin/cat不对其输入进行处理。它不会从中构建std::string,也不会扫描n,它只是执行一个又一个系统调用。

另一方面,您的程序构建string,复制它们,扫描n等。

这个小而完整的程序运行速度比/bin/cat:慢2-3个数量级

#include <string>
#include <iostream>
int main (int ac, char **av) {
  std::string crtLine;
  crtLine.reserve(1000);
  while(std::getline(std::cin, crtLine)) {
    std::cout << crtLine << "n";
  }
}

我这样计时:

$ time ./x < inputFile > /dev/null
$ time /bin/cat < inputFile > /dev/null


编辑该程序的性能在/bin/cat:的50%以内

#include <string>
#include <iostream>
#include <vector>
int main (int ac, char **av) {
  std::vector<char> v(4096);
  do {
    std::cin.read(&v[0], v.size());
    std::cout.write(&v[0], std::cin.gcount());
  } while(std::cin);
}

简而言之,如果您的需求是对输入执行逐行分析,那么您将不得不为使用格式化输入付出一些代价。另一方面,如果您需要执行逐字节的分析,那么您可以使用未格式化的输入,并且速度更快。

如果你真的想用stdin获得更好的性能,你应该尝试使用纯C.

vector<char> line(0x1000);
while(!feof(stdin))
    fgets(&line.front(), line.size(), stdin);

我认为更快的解决方案将基于sendfile