c++ 中将标准输入内容读入字符串或向量的最快方法

Posted

技术标签:

【中文标题】c++ 中将标准输入内容读入字符串或向量的最快方法【英文标题】:Fastest way in c++ to read contents of stdin into a string or vector 【发布时间】:2015-10-05 17:33:23 【问题描述】:

许多 *** 答案涉及如何从磁盘中获取文件,您可以在其中根据文件大小预分配内存。

What is the best way to read an entire file into a std::string in C++? Fastest way possible to read contents of a file

但是在 stdin 中使用 slurp 的最快方法是什么(例如,当一个大文件通过管道传输到您的程序中时)?

如果这是最快的解决方案,我很乐意将其转换为向量(以后总是可以转换为 std::string)。

【问题讨论】:

请衡量你自己,有许多可变性,C++ 标准未涵盖,但可能取决于实现。 @BenVoigt:哦,确实如此 - 然后继续 不使用字符串但 char * 和内存映射可能会给你更多的性能。但指定最快的方式。这高度依赖于系统 【参考方案1】:

将未格式化的数据读入内存的最快方法是使用未格式化的读取例程。例如 fstream::read()。没有什么能比得上它。

当心!有些人声称您会看到通过使用操作系统级别的例程(如 read())来提高性能。如果你尝试这个,你会得到一个巨大的性能下降。

编辑。对上述说法的一些解释。降级的原因是内核调用。每次读取都是内核调用,因此除非您准确读取最佳数据缓冲区的大小,否则您将招致对内核的更多调用或不太优化的读取。虽然您可以通过实验找出最佳读取大小,但 C 运行时已经为您完成了这项工作。 fread() 和未格式化的流读取已经进行了优化,因此无论您的读取块有多大,都可以保证以最优化的方式调用内核。

【讨论】:

当然,并非所有未格式化的读取例程都是一样的。 你有什么可以支持你最后的声明吗? @SergeyA 低级别的系统调用比无论如何都会使用该系统调用结束的函数要慢得多明显? @lilezek,因为缓冲 IO 与非缓冲 IO。 @SergeyA:“内核不执行多任务”???当然可以,你生活在哪个年代? “整个系统只能有一个 read() 调用”完全胡扯。【参考方案2】:

循环读取固定大小的缓冲区

令我惊讶的是,老式的、几乎类似于 c 的代码似乎是使用 clang 和 gcc 最快的:


    vector<char> cin_str;
    // 64k buffer seems sufficient
    std::streamsize buffer_sz = 65536;
    vector<char> buffer(buffer_sz);
    cin_str.reserve(buffer_sz);

    auto rdbuf = cin.rdbuf();
    while (auto cnt_char = rdbuf->sgetn(buffer.data(), buffer_sz))
        cin_str.insert(cin_str.end(), buffer.data(), buffer.data() + cnt_char);

使用istream::read()istream::gcount() 一样快,但需要一些额外的代码...

c++ 迭代器

令人惊讶的是,使用 istreambuf_iteratorunformatted 输入的迭代器)的速度要慢得多:对于某些测试文件,即使在关闭与 stdio 的同步之后也是如此,速度要慢得多。


    std::ios_base::sync_with_stdio(false) ;
    vector<char> cin_str;
    //              64k
    std::streamsize buffer_sz = 65536;
    cin_str.reserve(buffer_sz);

    std::istreambuf_iterator<char> iit (std::cin.rdbuf()); // stdin iterator
    std::istreambuf_iterator<char> eos;                    // end-of-range iterator
    std::copy(iit, eos, std::back_inserter(cin_str));
    return cin_str;

即使在为 vector 缓冲区保留空间之后也是如此(而不是仅仅分配给它)。

另一个惊喜是,即使缓冲区大小(64 kb)非常小,也能看到(接近)最大速度。 vector 只是有一个非常有效的重新分配策略。

附录:

Google-ing 找到了这篇博文

(http://insanecoding.blogspot.in/2011/11/reading-in-entire-file-at-once-in-c.html) 从 2011 年开始,这似乎表明这种方法与你在 c++ 中(在 gcc/clang 中)中的速度差不多,并且切换到 cstdio 并没有提供进一步的收益(但显然使代码甚至更丑!)。

避免复制

@BenVoigt 指出,如果我们明智地预先分配必要的空间,则可以通过sgetn() / istream::read() 将读取的数据放置到位:


    std::ios_base::sync_with_stdio(false) ;
    //              64k
    std::streamsize buffer_sz = 65536;
    vector<char> cin_str(buffer_sz);
    std::streamsize cin_str_data_end = 0U;

    auto rdbuf = cin.rdbuf();
    while (auto cnt_char = rdbuf->sgetn(cin_str_data_end + cin_str.data(), buffer_sz))
    
        cin_str_data_end += cnt_char;
        cin_str.resize(cin_str_data_end + buffer_sz);
    
    cin_str.resize(cin_str_data_end);
    return cin_str;

在测试中,这导致没有进一步的加速可能是因为这段代码主要由 1) i/o 2) 系统调用开销 3) 矢量内存分配

有没有更快的方法来做到这一点?来自 boost 的内存映射文件?

【讨论】:

您有数据备份吗?您使用了哪些编译设置?还有,为什么你的回答以问题结尾? 您无法对管道或 tty 进行内存映射,因此不太可能使用 stdin。您可以通过在较大的保留范围内提交额外的页面来发挥虚拟内存技巧来扩展缓冲区,从而避免在调整缓冲区大小期间复制数据。 istreambuf 的缓慢对于任何曾经分析过 C++ I/O 的人来说都不足为奇,iostreams 的糟糕性能是传奇。使用fread 甚至特定于操作系统的read 函数(在Windows 上,ReadFile)可能会提高速度。 你为什么感到惊讶? iostream 迭代器做格式化输入,当然它们会更慢。 您忘记关闭标准输入同步了吗? @SergeyA:如果你的评论说出你认为错误的地方会更有用......

以上是关于c++ 中将标准输入内容读入字符串或向量的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中将输入正确读入 2D 向量 vector<vector<int>>

如何在 C++ 中将整个文件读入 std::string?

在C ++中将用空格分隔的字符串读入向量[关闭]

将文件读入内存的最快技术?

在 C++ 中将某种结构化的文本文件读入数组

在 C++ 中将文件读入字符数组