[英]Best way to store string of known maximum length in file for fast load into vector<string> in C++
[英]C++ Fast way to load large txt file in vector<string>
我有一个文件〜12.000.000十六进制行和1,6GB文件的示例:
999CBA166262923D53D3EFA72F5C4E8EE1E1FF1E7E33C42D0CE8B73604034580F2
889CBA166262923D53D3EFA72F5C4E8EE1E1FF1E7E33C42D0CE8B73604034580F2
代码示例:
vector<string> buffer;
ifstream fe1("strings.txt");
string line1;
while (getline(fe1, line1)) {
buffer.push_back(line1);
}
现在加载大约需要20分钟。 有什么建议如何加快? 非常感谢。
将大型文本文件加载到std::vector<std::string>
效率不高且浪费,因为它会为每个std::string
分配堆内存并多次重新分配向量。 这些堆分配中的每一个都需要幕后的堆簿记信息(在64位系统上通常每个分配8字节) ,每行都需要一个std::string
对象(8-32字节,取决于标准库),因此,以这种方式加载的文件在RAM中要比在磁盘上占用更多的空间。
一种快速的方法是将文件映射到内存中,并实现迭代器以遍历其中的行。 这回避了上述问题。
工作示例:
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <iostream>
class LineIterator
: public boost::iterator_facade<
LineIterator,
boost::iterator_range<char const*>,
boost::iterators::forward_traversal_tag,
boost::iterator_range<char const*>
>
{
char const *p_, *q_;
boost::iterator_range<char const*> dereference() const { return {p_, this->next()}; }
bool equal(LineIterator b) const { return p_ == b.p_; }
void increment() { p_ = this->next(); }
char const* next() const { auto p = std::find(p_, q_, '\n'); return p + (p != q_); }
friend class boost::iterator_core_access;
public:
LineIterator(char const* begin, char const* end) : p_(begin), q_(end) {}
};
inline boost::iterator_range<LineIterator> crange(boost::interprocess::mapped_region const& r) {
auto p = static_cast<char const*>(r.get_address());
auto q = p + r.get_size();
return {LineIterator{p, q}, LineIterator{q, q}};
}
inline std::ostream& operator<<(std::ostream& s, boost::iterator_range<char const*> const& line) {
return s.write(line.begin(), line.size());
}
int main() {
boost::interprocess::file_mapping file("/usr/include/gnu-versions.h", boost::interprocess::read_only);
boost::interprocess::mapped_region memory(file, boost::interprocess::read_only);
unsigned n = 0;
for(auto line : crange(memory))
std::cout << n++ << ' ' << line;
}
您可以将整个文件读入内存。 这可以使用C ++流完成,或者您可以通过使用平台特定的API(例如,内存映射文件或它们自己的文件读取API)来获得更高的性能。
拥有此数据块后,为了提高性能,您要避免再复制并就地使用它。 在C ++ 17中,您具有std::string_view
,它与std::string
相似,但是使用现有的字符串数据,从而避免了复制。 否则,您可能只使用C样式char*
字符串,可以通过使用一对指针(开头/结尾)或指针和大小将换行替换为null( \\0
)。
在这里,我使用string_view
,我还假定换行符始终为\\n
并且末尾有换行符。 如果不是这种情况,则可能需要调整循环。 猜测vector
的大小也会获得一些性能,您可以通过文件长度来实现。 我也跳过了一些错误处理。
std::fstream is("data.txt", std::ios::in | std::ios::binary);
is.seekg(0, std::ios::end);
size_t data_size = is.tellg();
is.seekg(0, std::ios::beg);
std::unique_ptr<char[]> data(new char[data_size]);
is.read(data.get(), data_size);
std::vector<std::string_view> strings;
strings.reserve(data_size / 40); // If you have some idea, avoid re-allocations as general practice with vector etc.
for (size_t i = 0, start = 0; i < data_size; ++i)
{
if (data[i] == '\n') // End of line, got string
{
strings.emplace_back(data.get() + start, i - start);
start = i + 1;
}
}
为了获得更高的性能,您可以运行循环以使CPU与文件IO并行运行。 这可以通过线程或特定于平台的异步文件IO来完成。 但是,在这种情况下,循环将非常快,因此收益不多。
您可以简单地分配足够的RAM内存并几乎一次读取整个文本文件。 比起您可以通过内存指针访问RAM中的数据。 我在3秒钟内阅读了整个4GB文本文件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.