C++ ASCII数据导入:如何匹配Fortran';在C+中的大容量读取性能+;? 设置
您好,我有用于读取ASCII双精度数据的Fortran代码(问题底部的数据文件示例):C++ ASCII数据导入:如何匹配Fortran';在C+中的大容量读取性能+;? 设置,c++,fortran,ascii,C++,Fortran,Ascii,您好,我有用于读取ASCII双精度数据的Fortran代码(问题底部的数据文件示例): 和“等效”C++代码: #include <fstream> #include <vector> using std::ifstream; using std::vector; using std::ios; int main(){ int mx, my, mz; // Open the file 'CHGCAR' ifstream InFile('CHG
和“等效”C++代码:
#include <fstream>
#include <vector>
using std::ifstream;
using std::vector;
using std::ios;
int main(){
int mx, my, mz;
// Open the file 'CHGCAR'
ifstream InFile('CHGCAR', ios::in);
// Get the extent of the 3D system and allocate the 3D array
InFile >> mx >> my >> mz;
vector<vector<vector<double> > > charge(mx, vector<vector<double> >(my, vector<double>(mz)));
// Method 1: std::ifstream extraction operator to double
for (int i = 0; i < mx; ++i)
for (int j = 0; j < my; ++j)
for (int k = 0; k < mz; ++k)
InFile >> charge[i][j][k];
return 0;
}
for (int i = 0; i < mx; ++i)
for (int j = 0; j < my; ++j)
for (int k = 0; k < mz; ++k)
InFile >> charge[i][j][k];
执行与C++代码相同的任务:
#include <fstream>
#include <vector>
using std::ifstream;
using std::vector;
using std::ios;
int main(){
int mx, my, mz;
// Open the file 'CHGCAR'
ifstream InFile('CHGCAR', ios::in);
// Get the extent of the 3D system and allocate the 3D array
InFile >> mx >> my >> mz;
vector<vector<vector<double> > > charge(mx, vector<vector<double> >(my, vector<double>(mz)));
// Method 1: std::ifstream extraction operator to double
for (int i = 0; i < mx; ++i)
for (int j = 0; j < my; ++j)
for (int k = 0; k < mz; ++k)
InFile >> charge[i][j][k];
return 0;
}
for (int i = 0; i < mx; ++i)
for (int j = 0; j < my; ++j)
for (int k = 0; k < mz; ++k)
InFile >> charge[i][j][k];
同样,这比简单的基于操作符的方法要快得多,但仍然比Fortran版本慢得多——更不用说更多的代码了
如何获得更好的性能?
我确信,如果我要自己实现方法2,那么方法2就是一条路,但我很好奇如何提高性能以匹配Fortran代码。我正在考虑和正在研究的事情有:
- C++11和C++14特性
- 优化C或C++库,只做这种类型的事情
- 对方法2中使用的各个方法的改进
- 标记化库,例如,而不是
strtok()
- 与
atof()
- 标记化库,例如,而不是
特别是C++字符串工具包库将采用<代码>文件> <代码>和分隔符>代码> \n”/代码>并给我一个字符串标记对象(调用它<代码>文件> /CODE >,那么<<代码>循环的三进制代码>看起来像
for (int k = 0; k < Mz; ++k)
for (int j = 0; j < My; ++j)
for (int i = 0; i < Mx; ++i)
Charge[k][j][i] = FileTokens.nextFloatToken();
StrTk示例
下面是上面提到的。该场景正在解析包含3D网格信息的数据文件:
输入数据:
5
+1.0,+1.0,+1.0
-1.0,+1.0,-1.0
-1.0,-1.0,+1.0
+1.0,-1.0,-1.0
+0.0,+0.0,+0.0
4
0,1,4
1,2,4
2,3,4
3,1,4
代码:
结构点
{
双x,y,z;
};
结构三角形
{
标准:尺寸为i0、i1、i2;
};
int main()
{
std::string mesh_file=“mesh.txt”;
std::ifstream流(mesh_file.c_str());
std::字符串s;
//工艺点组
标准::德克点;
p点;
标准::大小\u t点\u计数=0;
strtk::解析线(流,“,点计数);
strtk::对于每条线(流,
点数,
[&点,&p](常量标准::字符串和行)
{
if(strtk::parse(第,,,行,p.x,p.y,p.z))
点。推回(p);
});
//处理三角形部分
三角形;
三角形t;
标准::大小\u t三角形\u计数=0;
strtk::解析线(流,“,三角形计数);
strtk::对于每条线(流,
三角计数,
[&三角形,&t](常量标准::字符串和线)
{
if(strtk::parse(行,,,t.i0,t.i1,t.i2))
三角形。推回(t);
});
返回0;
}
此
vector<vector<vector<double> > > charge(mx, vector<vector<double> >(my, vector<double>(mz)));
如果你的乐观主义者做得很好,这应该不会有什么帮助,但作为一种理智的检查,值得一试
如果你仍然比较慢,或者只是想尝试更快,你可以尝试优化你的数字解析:你说你的数据的格式都是ala0.23080516813E+04
——固定大小,你可以很容易地计算出要读入缓冲区的字节数,以便从内存中获得适当数量的值,然后你可以为每一个字节计算一个字节在
之后启动一个atol
以提取23080516813,然后将其乘以10的负幂(11(您的位数)减去04):为了提高速度,保留一个10的幂的表格,并使用提取的指数(即4)对其进行索引。(注意:在许多常见硬件上,乘以1E-7可能比除以1E7快。)
如果你想闪电式地解决这个问题,就改用内存映射文件访问。值得考虑的是,它比POSIX API(更不用说Windows)更容易使用,而且是可移植的,但是直接针对OS API编程也不难
更新-对第一和第二条评论的回应
使用boost内存映射的示例:
#include <boost/iostreams/device/mapped_file.hpp>
boost::mapped_file_params params("dbldat.in");
boost::mapped_file_source file(params);
file.open();
ASSERT(file.is_open());
const char* p = file.data();
const char* nl = strchr(p, '\n');
std::istringstream iss(std::string(p, nl - p));
size_t x, y, z;
ASSERT(iss >> x >> y >> z);
#包括
boost::mapped_file_params params(“dbldat.in”);
boost::映射的_文件_源文件(params);
file.open();
断言(file.is_open());
const char*p=file.data();
常量char*nl=strchr(p,'\n');
std::istringstream-iss(std::string(p,nl-p));
尺寸x,y,z;
断言(iss>>x>>y>>z);
上面将一个文件映射到地址p
的内存中,然后从第一行解析维度。从++nl
开始继续解析实际的双
表示。我在上面提到了一种方法,您关心的是数据格式的更改:您可以在文件中添加一个版本号,所以您可以使用优化解析,直到版本号更改,然后对“未知”文件格式采用通用的方式。就通用的方式而言,对于使用int chars_to_skip;double my_double;ASSERT的内存表示(sscanf(ptr),%f%n,&my_double,&chars_to_skip)=1)
是合理的:请参阅-然后可以通过字符到跳过将指针推进数据
接下来,您是否建议将reserve()
解决方案与引用创建解决方案相结合
对
还有(请原谅我的无知)为什么使用charge[i][j]
和v.emplace\u back()
要比charge[i][j].emplace\u back()好
这个建议是为了检查编译器是否没有对每个放置的元素重复计算charge[i][j]
:希望这不会对性能造成影响,您可以返回charge[i][j].emplace()
,但我认为这值得快速检查
最后,我对在每个循环的顶部使用空向量和reserve()表示怀疑。我有另一个程序使用该方法突然停止,用预先分配的多维向量替换reserve()大大加快了速度
这是可能的,但在一般情况下不一定是正确的,也不一定适用于这里——这在很大程度上取决于编译器/optimi
struct point
{
double x,y,z;
};
struct triangle
{
std::size_t i0,i1,i2;
};
int main()
{
std::string mesh_file = "mesh.txt";
std::ifstream stream(mesh_file.c_str());
std::string s;
// Process points section
std::deque<point> points;
point p;
std::size_t point_count = 0;
strtk::parse_line(stream," ",point_count);
strtk::for_each_line_n(stream,
point_count,
[&points,&p](const std::string& line)
{
if (strtk::parse(line,",",p.x,p.y,p.z))
points.push_back(p);
});
// Process triangles section
std::deque<triangle> triangles;
triangle t;
std::size_t triangle_count = 0;
strtk::parse_line(stream," ",triangle_count);
strtk::for_each_line_n(stream,
triangle_count,
[&triangles,&t](const std::string& line)
{
if (strtk::parse(line,",",t.i0,t.i1,t.i2))
triangles.push_back(t);
});
return 0;
}
vector<vector<vector<double> > > charge(mx, vector<vector<double> >(my, vector<double>(mz)));
for (int i = 0; i < mx; ++i)
for (int j = 0; j < my; ++j)
{
std::vector<double>& v = charge[i][j];
for (int k = 0; k < mz; ++k)
{
double d;
InFile >> d;
v.emplace_pack(d);
}
}
#include <boost/iostreams/device/mapped_file.hpp>
boost::mapped_file_params params("dbldat.in");
boost::mapped_file_source file(params);
file.open();
ASSERT(file.is_open());
const char* p = file.data();
const char* nl = strchr(p, '\n');
std::istringstream iss(std::string(p, nl - p));
size_t x, y, z;
ASSERT(iss >> x >> y >> z);