C ++多次写入文件的有效方法

时间:2013-03-19 14:37:11

标签: c++ performance io ofstream

我正在处理一个回调函数,根据回调中的数据,我想写入不同的文件。

例如,在一次调用中,我可能想要在使用不同数据的另一个调用中写入january.csv,它可能是july.csv。没有预先确定的顺序,可能是每个回调中的任何一个月,我无法提前知道。 january.csv(实际所有月份)将被多次写入。

这些回调发生得非常快,所以我需要这段代码尽可能高效。

我将采取的天真方法是每次使用以下代码:

ofstream fout;
fout.open(month_string);
fout<<data_string<<endl;
fout.close();

问题是,由于我不断打开/关闭month.csv文件,因此这看起来效率不高。有没有更快的方法我可以说保持january.csv,february.csv等一直打开以使其更快?

编辑:我在linux上写/ dev / shm,所以I / O延迟不是真正的问题。

3 个答案:

答案 0 :(得分:2)

您希望减少I / O呼叫的数量,同时在呼叫时充分利用它们。

例如,缓存数据并将更大的块写入文件。您可以使用另一个负责定期将缓冲区刷新到该文件的线程。

效率低下的基础是双重的:等待硬盘初始化(加快速度),第二个是定位文件和写入的空扇区。无论数据量多少,都会出现这种开销你在写作数据块越大,有效写入的时间就越多(盘片旋转时)。对于Flash / Thumb驱动器也是如此;拇指驱动器有开销(解锁,擦除等)。因此,目标是通过写入大块来减少开销。

您可能需要考虑使用数据库:Evaluating the need for database.

答案 1 :(得分:0)

我怀疑大多数系统会允许您同时打开~10K文件,这或多或少会排除打开所有文件并根据需要写入文件。

因此,您可能只需要创建某种proxy-ish对象来缓冲每个文件的数据,当缓冲区超过某个给定大小时,打开文件,将数据写入磁盘,然后再次关闭它

我可以看到两种相当简单的方法。一种是使用stringstream作为缓冲区自己编写大部分代码。客户端流向您的对象,该对象只是传递给stringstream。然后检查字符串流是否超过某个长度,如果是,则将内容写入磁盘并清空字符串流。

另一种方法是编写自己的文件缓冲区对象,该对象实现sync以打开文件,写入数据,然后再次关闭文件(通常会使文件始终保持打开状态)。

然后您将它们存储在std::map(或std::unordered_map)中,以便您从文件名到匹配的代理对象进行查找。

答案 2 :(得分:0)

我不认为一遍又一遍地打开和关闭同一个文件会很贵。操作系统通常设计为通过在内存中缓存部分FS元数据来处理该用例。成本主要是系统调用的上下文切换。另一方面,在10k文件上执行此操作可能会耗尽操作系统缓存功能。

您可以通过将所有带有目标的输出顺序写入单个文件中来形成日记,从而卸载一些FS工作。然后另一个程序(FS补充程序)将执行打开该日志的任务,缓冲写入命令(按文件对它们进行分组),然后在缓冲区达到某个阈值时将它们刷新到磁盘。您必须将日志中执行的命令标记为已提交,以便在补充程序中断且必须恢复的情况下,它将知道还有什么要做。


更新:

可以调整文件系统以支持同时打开和缓存10000个文件,并让它来处理调度命令的问题(这就是FS是制作 for。。

您的问题是为您的用例选择正确的文件系统。我建议用不同的FS进行测试,看哪哪个效果最好。

唯一剩下的部分是让您的程序使用std::map将文件名与其描述符相关联(微不足道)。

请参阅SO以进行调整linux max open files,或者如果您在特定FS上找不到该问题,可能会就该主题提出问题。