在c++中快速加载数值数据
Quickly loading numerical data in C++
我正在研究一个特征检测程序,它使用图像中各种地标的统计模型。该模型使用了大约100个不同的地标,每个地标的相关数据由16个双精度矩阵组成,每个矩阵的大小约为160x160。
我目前为每个地标使用一个文本文件,并将每个矩阵的值存储为空格分隔的行。为了读取数据,我每次从每个文件中读取一行,并将其传递给一个函数,该函数从该行生成一个stringstream,然后一次一个地从该流中读取矩阵的值。
在我的电脑上,这大约需要90秒来加载模型使用的~ 4000万倍。肯定有更快的方法来做这件事,但我没有从谷歌上找到任何有用的东西,我也没有这方面的经验。
如有任何建议,我将不胜感激。编辑:洛基让我张贴代码,所以我把它显示在下面。loadFromFile对每个地标被调用一次。每个里程碑文件的第一行说明模型为该里程碑使用了多少个级别(每个级别使用四个矩阵;默认情况下有四个级别)。这是一个可怕的混乱,但我不确定为什么这是如此惊人的慢。
void loadFromFile(string filename)
{
ifstream modelData(filename.c_str(), ifstream::in);
string line;
getline(modelData,line);
int numberOfLevels = atoi(line.c_str());
for(size_t ii = 0; ii < numberOfLevels; ++ii)
readProfileStats(modelData);
modelData.close();
}
void readProfileStats(ifstream& fileStream)
{
string line;
getline(fileStream, line);
Vector meanProfile = readMatrixFromString(line);
getline(fileStream, line);
Matrix principalComponents = readMatrixFromString(line);
getline(fileStream, line);
Matrix covarianceMatrixInverse = readMatrixFromString(line);
m_statsLevels.push_back(ProfileStats(meanProfile, principalComponents, covarianceMatrixInverse));
}
Matrix readMatrixFromString(const string& line)
{
stringstream stream(line);
size_t numRows;
size_t numCols;
stream >> numRows;
stream >> numCols;
Matrix matrix(numRows,numCols);
for(int ii = 0; ii < numRows; ++ii)
{
for(int jj = 0; jj < numCols; ++jj)
stream >> matrix(ii,jj);
}
return matrix;
}
尝试使用scanf库:
r1.cpp
> cat r1.cpp
#include <iostream>
int main()
{
double x;
long count = 0;
while(std::cin >> x)
{
++count;
}
std::cout << count << ": " << x << "n";
}
r2.cpp
> cat r2.cpp
#include <iostream>
#include <stdio.h>
int main()
{
double x;
long count = 0;
while(fscanf(stdin, "%lf", &x) == 1)
{
++count;
}
std::cout << count << ": " << x << "n";
}
结果串行
> g++ -O3 r1.cpp -o r1
> time (cat t | ./r1)
40000000: 9.36e+08
real 0m57.669s
user 0m56.992s
sys 0m1.688s
> g++ -O3 r2.cpp -o r2
> time (cat t | ./r2)
40000000: 9.36e+08
real 0m14.419s
user 0m13.897s
sys 0m1.352s
所以用IOstream读取40,000,000个数字花的时间比我预期的要长大约60秒。而使用scanf只需要15秒。所以大约快了4倍。
我做了同样的事情,只是将双精度数的二进制值写入文件。
注意,你必须把它们写成二进制,当然,你失去了所有的类型安全性和可移植性。
double x;
std::cout.write((char*)&x, sizeof(x));
r1b.cpp
> cat r1b.cpp
#include <iostream>
int main()
{
double x;
long count = 0;
while(std::cin.read((char*)&x, sizeof(double)))
{
++count;
}
std::cout << count << ": " << x << "n";
}
r2b.cpp
> cat r2b.cpp
#include <iostream>
#include <stdio.h>
int main()
{
double x;
long count = 0;
while(fread(&x, sizeof(double), 1, stdin) == 1)
{
++count;
}
std::cout << count << ": " << x << "n";
}
结果二进制
> time (cat t2 | ./r1b )
40000000: 9.36e+08
real 0m3.930s
user 0m3.592s
sys 0m0.984s
> time (cat t2 | ./r2b )
40000000: 9.36e+08
real 0m2.110s
user 0m1.840s
sys 0m0.804s
正如在注释中建议的那样,这里的问题是必须将数据从文本转换为数值。通过以二进制格式存储数据,可以完全消除这种情况。有一些库可以处理这个问题,比如hdf5。使用像这样的流行库有很多优点,因为您可以获得完整的预构建工具链,并支持除c++之外的许多其他语言。然而,缺点是在学习如何使用这些系统之前需要做很多工作。如果这是一个一次性的研究项目,我强烈建议您考虑一种不同的、更简单的方法:一旦第一次创建了结构,只需将数据结构写入或mmap到磁盘文件中。然后,创建一个函数,将二进制文件直接读入或mmap到您的数据结构中。为程序提供调用mmap函数而不是解析函数的选项。通过这种方式,您将看到显著的加速。
- 我的程序有一个保存配置文件的GUI,如何双击此配置文件以直接加载带有配置数据的GUI?
- 有效地将数据加载到 std::vector 中<char>
- C++WIN32-将RTF数据加载到Rich Edit控件
- 增强序列化,按基类型加载存档类会产生错误的数据
- 将数据从 c++ 加载到 QML Scatter3d 项
- 如何将训练数据从UCI加载到OpenCV?
- 将数据保存在C++中,从 Python 加载 - 推荐的数据格式
- 加载并从文件C 中加载大量的结构化数据
- 使用ifstream从DAT文件加载数据
- 将数据预加载到RAM中进行快速交易
- 将内存保存到文件并加载它而不必解析数据?
- 保存和加载类数据存储在C 中的二进制文件中
- 如何在Qt中从txt文件加载大数据
- c++在编译时加载大量数据
- 加载QPixmap数据的更好方法
- 在矢量 c++ 中加载大数据
- OpenGL-加载顶点数据时glBufferData()上的SegFault
- 在c++中快速加载数值数据
- 从文件加载"irregular"数据的最简单方法 C++
- 单元测试时从何处加载存根数据