【发布时间】:2011-07-19 22:57:31
【问题描述】:
我有大量浮点数形式的数据文本文件。我正在寻找用 C++ 读取它们的最快方法。如果这是最快的,我可以将文件更改为二进制文件。
如果您能给我提示或将我引至具有完整解释的网站,那就太好了。我不知道是否有任何图书馆可以快速完成工作。即使有任何开源软件可以完成这项工作,也会有所帮助。
【问题讨论】:
我有大量浮点数形式的数据文本文件。我正在寻找用 C++ 读取它们的最快方法。如果这是最快的,我可以将文件更改为二进制文件。
如果您能给我提示或将我引至具有完整解释的网站,那就太好了。我不知道是否有任何图书馆可以快速完成工作。即使有任何开源软件可以完成这项工作,也会有所帮助。
【问题讨论】:
拥有二进制文件是最快的选择。您不仅可以在单个操作中使用原始istream::read 直接在数组中读取它(这非常快),而且如果您的操作系统支持,您甚至可以将文件映射到内存中;您可以在 POSIX 系统上使用 open/mmap,在 Windows 上使用 CreateFile/CreateFileMapping/MapViewOfFile,甚至可以使用 Boost 跨平台解决方案(感谢 @Cory Nelson 指出)。
快速而肮脏的示例,假设文件包含一些 floats 的原始表示:
“正常”读作:
#include <fstream>
#include <vector>
// ...
// Open the stream
std::ifstream is("input.dat");
// Determine the file length
is.seekg(0, std::ios_base::end);
std::size_t size=is.tellg();
is.seekg(0, std::ios_base::beg);
// Create a vector to store the data
std::vector<float> v(size/sizeof(float));
// Load the data
is.read((char*) &v[0], size);
// Close the file
is.close();
使用共享内存:
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
using boost::interprocess;
// ....
// Create the file mapping
file_mapping fm("input.dat", read_only);
// Map the file in memory
mapped_region region(fm, read_only);
// Get the address where the file has been mapped
float * addr = (float *)region.get_address();
std::size_t elements = region.get_size()/sizeof(float);
【讨论】:
read 会打败它。
mmap 不会比read 慢。
您的瓶颈在于 I/O。您希望程序以最少的 I/O 调用将尽可能多的数据读入内存。例如用一个fread 读取256 个数字比一个数字的256 个fread 快。
如果可以的话,格式化数据文件以匹配目标平台的内部浮点表示,或者至少是你的程序的表示。这减少了将文本表示转换为内部表示的开销。
如果可能,绕过操作系统并使用 DMA 控制器读取文件数据。 DMA 芯片将数据读入内存的负担从处理器的肩上卸下。
压缩你的数据文件。数据文件希望位于磁盘上的一组连续扇区中。这将减少在物理盘片上寻找不同区域所花费的时间。
您的程序是否要求对磁盘资源和处理器进行独占控制。阻止所有其他不重要的任务;提高程序执行的优先级。
使用多个缓冲区来保持磁盘驱动器旋转。大部分时间都花在等待硬盘驱动器加速和减速上。您的程序可能正在处理数据,而其他东西正在将数据存储到缓冲区中,这会导致...
多线程。创建一个线程读取数据并在缓冲区不为空时提醒处理任务。
这些应该会让你忙一阵子。所有其他优化将导致可忽略不计的性能提升。 (例如直接访问硬盘控制器以传输到您的缓冲区之一。)
【讨论】:
另外注意编译模式。 我试过用 1M 行解析一个文件。调试模式需要 50 秒来解析数据并附加到我的容器中。释放模式消耗至少快十倍,大约 4 秒。 下面的代码是在使用 istringstream 将数据解析为 2D 点 (,) 之前读取整个文件。
vector <float> in_data;
string raw_data;
ifstream ifs;
ifs.open(_file_in.c_str(), ios::binary);
ifs.seekg(0, ios::end);
long length = ifs.tellg();
ifs.seekg(0, ios::beg);
char * buffer;
buffer = new char[length];
ifs.read(buffer, length);
raw_data = buffer;
ifs.close();
delete[]buffer;
cout << "Size: " << raw_data.length()/1024/1024.0 << "Mb" << endl;
istringstream _sstr(raw_data);
string _line;
while (getline(_sstr, _line)){
istringstream _ss(_line);
vector <float> record;
//maybe using boost/Tokenizer is a good idea ...
while (_ss)
{
string s;
if (!getline(_ss, s, ',')) break;
record.push_back(atof(s.c_str()));
}
in_data.push_back(record[0]);
}
【讨论】: