我有一个非常大的二进制文件,我需要根据输入文件中的id创建单独的文件。有146个输出文件,我正在使用cstdlib
和fopen
以及fwrite
。 FOPEN_MAX
为20,因此我无法同时打开所有146个输出文件。我还想最小化打开和关闭输出文件的次数。
如何有效地写入输出文件?
由于遗留代码,我还必须使用cstdlib
库。
可执行文件还必须是UNIX和Windows跨平台兼容的。
答案 0 :(得分:5)
您可能采取的几种方法:
保持打开的输出文件句柄的缓存小于FOPEN_MAX - 如果需要对已经打开的文件进行写入,则只需执行写入操作。否则,关闭缓存中的一个句柄并打开输出文件。如果您的数据通常在一组特定文件的数据方面聚集在一起在输入文件中组合在一起,这应该适用于文件句柄缓存的LRU策略。
自己处理输出缓冲,而不是让库为你做:保留自己的146(或者你可能需要多少)输出缓冲区并将输出缓冲到那些,并执行打开/刷新/当特定输出缓冲区被填满时关闭。您甚至可以将其与上述方法结合起来,以最大限度地减少开/关操作。
请确保您测试填充或接近填充输出缓冲区时可能发生的边缘条件。
答案 1 :(得分:3)
也可能值得扫描输入文件,制作每个输出id的列表并对其进行排序,以便首先编写所有file1条目,然后编写所有file2条目等。
答案 2 :(得分:1)
如果无法以某种方式增加最大FOPEN_MAX,您可以创建一个简单的请求队列,然后根据需要关闭并重新打开文件。
您还可以跟踪每个文件的上次写入时间,并尝试保持最近写入的文件处于打开状态。
答案 3 :(得分:0)
解决方案似乎很明显 - 打开N个文件,其中N略低于FOPEN_MAX。然后读取输入文件并提取前N个输出文件的内容。然后关闭输出文件,倒回输入,然后重复。
答案 4 :(得分:0)
首先,我希望你尽可能地并行运行。没有理由不能同时写入多个文件。我建议你做thomask所说的并排队请求。然后,您可以使用某些线程同步来等待整个队列被刷新,然后再允许下一轮写入。
答案 5 :(得分:0)
您尚未提及“实时”写入这些输出是否至关重要,或者正在写入多少数据。根据您的约束,一个选项可能是缓冲所有输出并在软件运行结束时写入它们。
这种方法的一个变体是设置固定大小的内部缓冲区,一旦达到内部缓冲区限制,打开文件,追加和关闭,然后清除缓冲区以获得更多输出。缓冲区减少了打开/关闭周期的数量,并为您提供了文件系统通常可以很好地处理的写入突发。这适用于需要稍微实时写入和/或数据大于可用内存且文件句柄超出系统最大值的情况。
答案 6 :(得分:0)
您可以分2步完成。
1)将前19个ID写入一个文件,将下一个19个ID写入下一个文件,依此类推。因此,您需要为此步骤并行打开8个输出文件(和输入文件)。
2)对于每个如此创建的文件,创建19个(最后一个只有13个)新文件并将id写入其中。
无论输入文件有多大以及它包含多少个id数据集,您始终需要打开和关闭163个文件。但是你需要把数据写两次,所以如果id数据集真的很小并随机分布,它可能只值得。
我认为在大多数情况下,更频繁地打开和关闭文件会更有效。
答案 7 :(得分:0)
最安全的方法是在写入后打开文件并刷新,如果不再进行最近的写入则关闭。程序控制之外的许多东西都可能破坏文件的内容。在阅读时请记住这一点。
我建议保留std::map
或std::vector
FILE
个指针。 map
允许您通过ID访问文件指针。如果ID范围很小,您可以创建vector
,保留元素,并使用ID作为索引。这将允许您同时打开许多文件。注意数据损坏的概念。
同时打开文件的限制由操作系统设置。例如,如果您的操作系统最多为10,则在请求第11个文件时您将进行安排。
另一个技巧是每个文件的动态内存中的保留缓冲区。处理完所有数据后,打开一个文件(或多个文件),写入缓冲区(使用一个fwrite
),关闭并继续。这可能会更快,因为您在数据处理期间写入内存而不是文件。一个有趣的附注是,您的操作系统也可以将缓冲区分页到硬盘驱动器。缓冲区的大小和数量是一个与平台相关的优化问题(您必须进行调整和测试以获得良好的组合)。如果操作系统将内存分页到磁盘,您的程序将会变慢。
答案 8 :(得分:0)
好吧,如果我用OP中列出的约束来编写它,我会创建146个缓冲区并将数据放入其中,然后在最后,依次遍历缓冲区并关闭/打开单个文件句柄。
你在评论中提到速度是一个主要问题,而天真的方法太慢了。
您可以开始考虑一些事情。一种是将二进制文件重组为顺序条带,这将允许并行操作。另一种是最近最少使用的文件句柄集合方法。另一种方法可能是分叉到8个不同的进程,每个进程输出到19-20个文件。
根据二进制组织(高度分散与高度顺序),这些方法中的一些或多或少都是可行的。
主要限制因素是二进制数据的大小。它比缓存更大吗?比记忆更大?从录音带里流出来?不断脱离传感器流并仅作为内存中的“文件”存在?每个都提出了不同的优化策略......
另一个问题是使用模式。您是否偶尔会对文件进行尖峰写入,或者您是否只写了几次大量的块?这决定了文件句柄的不同缓存/分页策略的有效性。
答案 9 :(得分:0)
假设您使用的是* nix系统,则限制是针对每个进程,而不是系统范围。这意味着你可以启动多个进程,每个进程负责你要过滤的id的一个子集。每个进程都可以保持在FOPEN_MAX范围内。
您可以让一个父进程读取输入文件,然后通过管道特殊文件将数据发送到各种“写入”进程。
答案 10 :(得分:0)
要实现最小数量的文件打开和关闭,您必须多次读取输入。每次,您都会选择需要排序的ID子集,并且只将这些记录提取到输出文件中。
每个线程的伪代码:
fseek()
回到输入的开头。fseek()
到输入的开头。这个方法对多个线程的效果不太好,因为最终线程将完全读取文件的不同部分。当发生这种情况时,文件缓存很难有效。您可以使用障碍来保持线程或多或少的锁定步骤。
您可以使用多个线程和一个大缓冲池来只进行一次输入。这是以更多文件打开和关闭(可能)为代价的。每个线程都会,直到整个文件被排序:
fwrite()
将其附加到文件中。如果没有,请等到最低(希望这不会发生太多)。您可以将刷新输出文件的单位更改为磁盘。也许你有足够的RAM来按输出文件一次收集200页?
要小心的事情:
fwrite()
同时输出到同一个输出文件。如果发生这种情况,您可能会损坏其中一个页面。