C++ 在C+中处理简单但较大文件的有效方法+;

C++ 在C+中处理简单但较大文件的有效方法+;,c++,C++,我正在做一个项目,在性能方面让我有点不知所措。我的任务是读取粒子坐标的大文件(50MB左右)并显示它们。我想用C++来学习,因为我已经在学习它了。p> 文件中的坐标结构很简单,有很多(比如一百万左右): 作为一个noob,我只想逐行读取文件并将它们存储在向量中,这是错误的吗?也许我应该先读取整个文件(缓冲?),然后解析值 工作示例: clock_t c1 = clock(); vector<double> coords; double coord; ifstream fin("fil

我正在做一个项目,在性能方面让我有点不知所措。我的任务是读取粒子坐标的大文件(50MB左右)并显示它们。我想用C++来学习,因为我已经在学习它了。p> 文件中的坐标结构很简单,有很多(比如一百万左右):

作为一个noob,我只想逐行读取文件并将它们存储在向量中,这是错误的吗?也许我应该先读取整个文件(缓冲?),然后解析值

工作示例:

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;

这在我看来很快,但我认为我的思维不是一个好的判断。

如果你知道“平均”文件有多大,你可能想使用.reserve预先分配向量

效率是一个棘手的游戏。不要过早地耍花招,设计一个好的基本算法。如果速度不够快,则开始查看IO例程,无论是创建任何“额外”对象(显式还是隐式,尤其是在传递参数时)


在您的示例中,您可能希望在打印摘要输出之前对clock()进行第二次调用——获得更精确的计时!)

改进它的一种方法是,不必逐行读取,您可以读取主内存中的文件块,然后从缓冲区中“逐行”读取。缓冲区的大小可以是计算机中群集的大小。

在某些平台上,您可以通过使用C标准的Libaray I/O(
)来获得更好的性能

但除此之外,如果您知道从文件中读取了多少值(或者您可以将计数包含在文件本身中),则可以通过初始化适当大小的向量并直接写入迭代器来提高性能,如下所示:

std::vector<double> coords(4000000); 
std::vector<double>::iterator it = coords.begin();
ifstream fin("file.txt");
while (fin >> *(it++)) {}
std::向量坐标(4000000);
std::vector::iterator it=coords.begin();
ifstream fin(“file.txt”);
而(fin>>*(it++){}

只要确保您可以依赖计数,否则您就有可能迭代到向量的末尾。

如果您的输入文件具有固定的线宽(每行字符数),那么将数据拖入缓冲区会变得更容易。对于可变线宽文件,确定何时重新填充缓冲区变得很棘手

单缓冲区法 分配一个
字符的缓冲区,大小=(线宽)*要存储的行数。或者可以反向工作:缓冲区中的最大行数=(缓冲区大小)/(线宽)。请记住,线宽可能包含“
”\n'、“\r”
或两者

使用
fread
istream::Read
读取整个缓冲区,否则称为块读取。 在缓冲区中保留指向“下一行”的指针。读取后递增指针,如果指针超出缓冲区的末尾,则读入另一个块。必要时重复

双缓冲区法 分配两个缓冲区。读取一个缓冲区。开始处理第一个缓冲区中的数据。在处理第一个缓冲区时读入第二个缓冲区。当第一个缓冲区处理完成后,开始处理第二个缓冲区并读入第一个缓冲区。必要时重复

这种方法依赖于多重处理:边读边处理数据。这可以实现为两个线程,或者非阻塞读取(读取操作不会在返回之前等待完成)。对于线程,一个线程进行处理,而另一个线程进行读取。使用信号量到单端处理和缓冲区就绪

多缓冲区法 类似于双缓冲区,但分配了更多缓冲区。把这看作是一场比赛。阅读任务试图领先于处理任务。处理任务应该等到读取了N个缓冲区后再开始。目标是分配足够的缓冲区,以便处理任务不会等待读卡器任务

优化:循环展开

你也可以考虑循环展开作为另一个速度优化。在循环中多次复制代码以减少迭代次数


我通常等到程序正常运行后再应用这些技术,除非程序速度非常慢,例如程序需要1小时来处理2GB文件(优化时间减少到2分钟)。

优化应该从测量开始。 描述的代码有三个功能:

  • 从文件中读取一些字节
  • 将它们解析为双精度的文本表示形式
  • 将它们存储在vector中
  • 应测量每个部件的时间分配

    对于#1,它可以将文件内容预读入内存(50M应该合适,并且不会导致交换),然后运行带有时间测量的算法。差异将显示从磁盘读取所需的时间

    对于#2,文件结构暂时可以切换为二进制表示的双精度序列。差异将显示解析所需的时间

    对于#3的测量,可以使用std::vector::reserve(实际上,正如已经建议的那样,这不仅可以作为测量工具,还可以作为优化)

    就我个人而言,我不认为#3会花费大量时间,但测量比猜测好


    在检测到最耗时的零件后,工作可能会变得更加集中,从而更加有效……

    除了已经给出的答案之外,您可能会考虑如何显示坐标。在进一步处理之前,您可能不需要将它们存储在向量中?

    问题是,它是否足够快?如果是这样,你有你的解决方案。我和尼尔在一起。某个地方的人必须读取一次数据,然后你可以玩一些小把戏,比如mmap到文件,使用谷歌的ProtoBuf之类的东西。。。但您必须至少一次将人类可读的ascii转换为二进制。由于1.7秒并没有那么长,为了清晰起见,您最好保留它。此外,如果输入文件的大小增加(正如这些事情往往发生的那样),则此解决方案可以很好地扩展,从而读取整个数据
    done. 2000000 coords read.
    took 1.74 seconds.
    
    std::vector<double> coords(4000000); 
    std::vector<double>::iterator it = coords.begin();
    ifstream fin("file.txt");
    while (fin >> *(it++)) {}