【问题标题】:Fastest and efficient way of parsing raw data from file从文件中解析原始数据的最快和最有效的方法
【发布时间】:2012-12-02 20:36:59
【问题描述】:

我正在做一些项目,我想知道哪种方式最有效地从文件中读取大量数据(我说的是 100 行到大约 30 亿行的文件,可以多想想)。读取后,数据将存储在结构化数据集中(vector<entry> 其中“entry”定义了结构化行)。

此文件的结构化行可能如下所示: string int int int string string 它也以适当的平台EOL 结尾,并以TAB 分隔

我希望完成的是:

  1. 将文件读入内存(string)或vector<char>
  2. 从我的缓冲区中读取原始数据并将其格式化到我的数据集中。

我需要考虑内存占用并具有快速的解析率。 我已经避免使用stringstream,因为它们看起来太慢了。

我还避免了对我的文件的多次 I/O 调用,方法是:

// open the stream
std::ifstream is(filename);

// determine the file length
is.seekg(0, ios_base::end);
std::size_t size = is.tellg();
is.seekg(0, std::ios_base::beg);

// "out" can be a std::string or vector<char>
out.reserve(size / sizeof (char));
out.resize(size / sizeof (char), 0);

// load the data
is.read((char *) &out[0], size);

// close the file
is.close();

我考虑过使用这个巨大的std::string,然后逐行循环,我会将行信息(字符串和整数部分)提取到我的数据集行中。有没有更好的方法来做到这一点?

编辑:此应用程序可以在 32 位、64 位计算机上运行,​​或者在超级计算机上运行更大的文件。

非常欢迎任何建议。

谢谢

【问题讨论】:

  • 我不是这个领域的专家,但是你需要非常小心的是你的向量!如果您要存储条目而不是指针(智能),那么每次调整矢量大小时,您最终都会复制所有对象。如果您的原始文件为您提供了带有项目数的标题,请确保 reserve 这些元素的向量
  • 使用如此简单的格式,解析速度是无关紧要的。无论您做什么,它仍然会比从光盘中实际读取文件快一千倍(!)。由于内存消耗,一次性将文件读入内存也不是一个特别好的解决方案。但是,将输入分块会有所帮助。如果你真的需要高性能,那么内存映射文件就没有办法了。
  • 所有常见的 I/O 库(如 C++ 流和 C stdio)都会为您进行缓冲。大多数操作系统会发现您正在按顺序读取一个大文件并为您提前读取。所有的调优都应该是数据驱动的。因此,编写一段干净的代码,然后对其进行概要分析,以了解时间的去向。
  • Raymond Chen 有一个关于优化类似场景部分的迷你系列01234(也有部分 5 和 6)。略有不同,但注意事项应该相同。
  • 假设你的 3 个字符串是 1 个字节长,每个 int 是 4 个字节,每个条目至少需要 18 个字节。对于 30 亿行数据,您所说的内存是 54 GB。这不适合 32 位进程。对于 64 位进程,大多数台式机(具有 8 GB RAM)将开始抖动并变得非常慢。

标签: c++ performance multiplatform


【解决方案1】:

一些随意的想法:

  • 在开头使用vector::resize()(你这样做了)
  • 一次读取大块文件数据,至少 4k,最好是 256k。将它们读入内存缓冲区,将该缓冲区解析为您的向量。
  • 不要一次读取整个文件,这可能会导致不必要的交换。
  • sizeof(char) 始终为 1 :)

【讨论】:

  • 谢谢,我想读取大块数据,然后我会这样做。
【解决方案2】:

虽然我不能代表具有 3 gig 线的超级计算机,但在台式机上的内存中将无处可去。

我认为您应该首先尝试弄清楚对该数据的所有操作。您应该尝试将所有算法设计为按顺序运行。如果您需要随机访问,您将一直进行交换。这种算法设计将对您的数据模型产生重大影响。

所以不要从读取所有数据开始,只是因为这是一个简单的部分,而是设计整个系统时清楚地了解整个处理过程中内存中的数据。


更新
当您在流的单次运行中进行所有处理并分阶段(读取 - 预处理 - ... - 写入)分离数据处理时,您可以有效地利用多线程。


终于

  • 无论您想在数据循环中执行什么操作,都应尽量减少循环次数。确保您可以在读取循环中进行平均。
  • 立即组成一个测试文件,大小和时间两种不同的方法是您期望的最坏情况

.

time
loop
    read line from disk
time
loop
    process line (counting words per line)
time
loop
    write data (word count) from line to disk
time

对。

time
loop
    read line from disk
    process line (counting words per line)
    write data (word count) from line to disk
time

如果您的算法已经在使用您的算法。否则组成一个(比如每行计算单词)。如果写入阶段不适用于您的问题,请跳过它。这个测试确实花费你不到一个小时的时间来写,但可以为你节省很多。

【讨论】:

  • 是的,所有算法都是按顺序设计的,所以我应该可以很好地进行内存交换,我将读取数据块而不是整个文件。
  • 您的问题和代码 sn-p 建议您要一次读取整个文件。您所做的是将一个多演出文件读入内存,该内存必须交换到磁盘上。然后你关闭文件。从技术上讲,你什么也没做。您在内存中有一些演出(来自文件的尾部),其余数据(尤其是开头)驻留在磁盘上。如果您的算法开始处理您的数据,则必须换入数据。您等待磁盘。
  • 相反,如果你读取一行,预处理它,处理它,后处理它,用它做魔术,将结果存储到磁盘并继续读取下一行,你的内存占用很少,并且总处理时间是一次完整读取或一次完整写入或一次完整处理,无论哪个更大。尽量不要充当热水容器,充当连续流加热器。当然,您不应从流中读取单个字符,但 getline() 应该没问题(因为它已经提供了一些您必须做的解析)
  • 谢谢,我会考虑所有这些,但我仍然需要在内存中加载完整的数据,因为我需要根据平均值和标准差计算配置文件
猜你喜欢
  • 2019-10-20
  • 2014-06-02
  • 1970-01-01
  • 1970-01-01
  • 2012-05-28
  • 1970-01-01
  • 2012-09-29
  • 2013-03-23
  • 1970-01-01
相关资源
最近更新 更多