【发布时间】:2016-02-12 23:36:56
【问题描述】:
所以我试图为自己编写一个用于 linux 管道的命令。可以把它想象成 gnu 'cat' 或 'sed' 的复制品,它从标准输入中获取输入,进行一些处理并写入标准输出。
我最初编写了一个 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
我尝试了 cin.getline(buffer, size) 而不是 getline(istream, string) 但没有任何改进。这很尴尬,是缓冲问题吗?我还尝试一次获取 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 搞砸了时机。
【问题讨论】:
-
filter-range.sh中还有什么?你为什么不直接调用你的 C++ 程序呢?此外,该循环的典型模式是while(std::getline(std::cin, crtLine)) { std::cout << crtLine << "\n"; },但更改它不会影响您的问题。 -
如果你追求性能,你应该尝试 C 风格的 I/O 函数而不是 cin/cout ;)
-
你编译优化了吗? -O2 还是 -O3?这可能不会减少 44 秒,但如果您担心时间问题,绝对应该这样做。
-
Rob:是的,你是对的,你的版本是等效的并且更漂亮。我使用它,直接调用脚本,没有改变,我的程序什么也不做。我正在使用 g++ -O3 -Wall -c -fmessage-length=0 -MMD -MP
-
您会惊讶于您的通用命令比专门设计用于快速执行某些操作的专用工具要慢。如果你写了一个命令,那就是甜菜猫,那么它就是新的猫。由于您正在进行行处理,我希望您(在您进行大量优化之后)获得与任何基于行的 unix 过滤器相同的速度。