我正在开展一个项目,让我在性能方面略胜一筹。我的任务是读取大(50MB左右)的粒子坐标文件并显示它们。我想使用C ++,因为我已经在学习它了。
文件中的坐标结构很简单,只有很多(比如一百万左右):
1234.5667 5234.1566 //coordinate 1
8532.6123 5152.6612 //coordinate 2
....
作为一个菜鸟,我只是想逐行读取文件并将它们存储在向量中,这是错的吗?也许我应该首先读取整个文件(缓冲?),然后解析值?
工作示例:
clock_t c1 = clock();
vector<double> coords;
double coord;
ifstream fin("file.txt");
while(fin >> coord) {
coords.push_back(coord);
}
cout << "done. " << coords.size()/2 << " coords read.\n";
cout << "took " << (clock() - c1)/(double)CLOCKS_PER_SEC << " seconds." << endl;
具有200万坐标的40MB文件上的相应输出:
done. 2000000 coords read.
took 1.74 seconds.
在我看来这很快,但我认为我的思想不是一个好的判断。
答案 0 :(得分:3)
如果您知道“平均”文件的大小,可能需要使用.reserve预先分配矢量。
效率是一个棘手的游戏。不要在早期玩弄技巧,并设计一个好的基本算法。如果它不够快,你就开始查看IO例程,无论你是创建任何“额外”对象(显式或隐式,特别是如果你传递参数)。
在您的示例中,您可能希望在打印摘要输出之前再次调用clock() - 获得稍微更精确的计时! :)
答案 1 :(得分:3)
如果您的输入文件具有固定的行宽(每行字符数),则将数据拖入缓冲区变得更加容易。对于可变行宽文件,确定何时重新填充缓冲区变得棘手。
分配char
,size =(线宽)*要存储的行数的缓冲区。或者您可以向后工作:缓冲区中的最大行=(缓冲区大小)/(行宽)。请记住,线宽可能包含'\n', '\r'
或两者。
使用fread
或istream::read
读取整个缓冲区,也称为块读取。
保持指向缓冲区中“下一行”的指针。读取后递增指针,如果超出缓冲区末尾,则读取另一个块。根据需要重复。
分配两个缓冲区。读一个缓冲区。开始处理第一个缓冲区中的数据首先处理时读入第二个缓冲区。第一次缓冲处理完成后,开始处理第二个缓冲区并先读入。根据需要重复。
此方法依赖于多处理:处理数据时读取。这可以实现为两个线程,或非阻塞读取(读取操作不会在返回之前等待完成)。使用线程,一个线程处理,而另一个线程读取。使用信号量来处理单个结束和缓冲就绪。
与双缓冲区类似,但分配了更多缓冲区。考虑这是一场比赛。阅读任务试图保持领先于处理任务。处理任务应该等到启动前读取N个缓冲区。目标是分配足够的缓冲区,以便处理任务不等待读者任务。
您可能还想将循环展开视为另一种速度优化。多次复制循环中的代码以减少迭代次数。
我通常会等到程序正常运行之后再应用这些技术,除非程序速度非常慢,例如程序需要1小时才能处理2GB文件(优化时间为2分钟)。
答案 2 :(得分:1)
优化应从测量开始。 描述的代码做了三件事:
应衡量每个部分的时间份额。
对于#1,它可以将文件内容预读到内存中(50M应该适合并且不会导致交换),然后运行带有时间测量的算法。差异将显示从磁盘读取的数量。
对于#2文件结构,暂时可以切换到双精度二进制表示的序列。差异将显示解析需要多少。
对于#3 std :: vector :: reserve的测量可以使用(实际上已经建议,这不仅可以是测量工具,还可以是优化)。
我个人认为#3不会占用大量时间,但测量优于猜测。
在检测到大部分时间后,部分工作可能会变得更集中,从而更有效......
答案 3 :(得分:0)
您可以想到改进它的一种方法是,您可以读取主存储器中的文件块,然后从该缓冲区中逐行读取,而不是逐行读取。缓冲区的大小可以是计算机中群集的大小。
答案 4 :(得分:0)
在某些平台上,通过回退到C Standard Libaray I / O(<cstdio>
),您将获得更好的性能。
但是,如果您知道从文件中读取了多少个值(或者您可以在文件本身中包含计数),那么通过初始化适当大小的向量,您可以通过可测量的数量来提高性能并直接写入迭代器,如下所示:
std::vector<double> coords(4000000);
std::vector<double>::iterator it = coords.begin();
ifstream fin("file.txt");
while (fin >> *(it++)) {}
确保您可以依赖计数,否则您可能会在向量的末尾迭代。
答案 5 :(得分:0)
除了已经给出的答案,您可能会考虑如何显示坐标。也许您不需要在进一步处理之前将它们存储在矢量中?