【发布时间】:2020-07-21 06:24:28
【问题描述】:
我目前正在使用 fstream 通过 C++ 加载 7.1GB 的数据。 .txt 文件如下所示:
item1 2.87 4.64 ...
item2 5.89 9.24 ...
... ... ... ...
它有 300000 行和 201 列(1 列用于项目名称,200 用于其权重),每个单元格都有一个双精度类型编号。我现在的做法是这样的:
ifstream click_log(R"(1.txt)", ifstream::in);
string line;
unordered_map<string, vector<double>> dict;
while (getline(click_log, line)){
istringstream record(line);
string key;
vector<double> weights;
double weight;
record >> key;
while (record >> weight){
weights.push_back(weight);
}
dict[key] = weights;
}
但是,我的电脑(AMD 3700X,8 核)大约需要 30 分钟才能完全加载文件。 它之所以慢是因为它的 O(m*n) 复杂性,还是仅仅是将字符串转换为双精度的事实很慢? 从 .txt 加载数据最有效的方法是什么?
【问题讨论】:
-
大多数 C 编译器也有一个分析工具。也许试着找出大部分时间都花在了哪里。我怀疑字典可能不是在这种情况下使用的最佳数据结构。使用老式的线性链表(而不是预先分配大小的数组)怎么样?另外,如果你只是从文件中读取所有行(丢弃它们),那是什么时候?
-
首先,您应该预先分配您的向量或使用
std::array,因为大小是众所周知的(字典也是如此)。由于std::vector机器,它将删除大量释放/重新分配/副本。然后你应该直接写入目标容器而不是使用临时向量(并做很多额外的副本)。但是我有一个问题,您的文件是否也包含“+-----+”?还是您添加它是为了便于阅读?这很令人困惑,因为您的代码似乎无法处理。 -
毫无疑问,在读取权重之前调用
weights.reserve(200)将是一个显着的改进。这将具有矢量不会过大的额外好处。其次,使用移动语义将其添加到地图将有助于防止额外的副本。最后,在unordered_map上保留足够的桶将有助于避免重新散列,这也很昂贵。如果您不确定,请考虑使用std::map作为替代方案。 -
还要注意,即使从字符串中解析
double值也是一笔不小的开销。如果您知道您的数据始终符合特定格式(例如始终为十进制表示法),请考虑滚动您自己的简化double值解析器,如果在进行这些其他优化后分析显示它很重要。而且,如果您可以不用存储float而不是double(假设您不需要高精度),那么这几乎可以将您的内存占用减半。 -
这里的另一个考虑因素是,在处理文件的每一行之前,您完全是在执行此单线程操作并等待阻塞 I/O 操作。您应该能够在所有 CPU 线程之间分配这项工作。应该有一个可以读取的环境变量来确定 CPU 线程的数量(并不总是与内核数量相同),并且现代存储设备可以并行执行多个读取,因此值得研究 imo。