将数据写入文件:fflush()需要很长时间

3

我有一个需求,需要缓冲大量数据(以GB为单位),以备将来使用。由于没有足够的RAM来缓冲这么多数据,所以我决定将数据存储在文件中。

现在的问题是,在我将数据写入文件时,其他线程可能需要那些“缓冲”数据,因此每次写入数据时都必须刷新文件流。确切地说,数据是视频帧,我将其作为预录制数据(就像 TiVo 一样)进行缓冲,其他线程可能会在任何给定时间点上想要写入它,但当他们这样做时,他们从文件中读取并处理帧。

通常情况下,fwrite-fflush 组合需要大约 150 微秒,但偶尔(而且相当经常),这个组合需要超过 1.5 秒。我不能承受这样的延迟,因为我必须实时处理帧。

我有很多问题:

  1. 我的缓冲数据到文件的方法正确吗?我还有哪些备选方案?

  2. 你有什么想法,为什么 fwrite-fflush 操作有时突然需要更长的时间?请注意,它在一次 1.5 秒后会恢复到 150 微秒。

2个回答

2
关于#2:现代大多数文件系统使用B树方法来管理当今巨大硬盘驱动器中的目录和数据节点的数量。与所有B树一样,它们有时需要平衡。在此过程中,不应发生任何更改,因此系统会锁定。通常,由于操作系统具有巨大的缓存,这并不是什么大问题,但您是其中一个特例,因此会受到影响。
你能做些什么呢?有两种方法:
1. 使用套接字进行通信,并将最后N帧保留在RAM中(即从不将它们写入磁盘或使用独立进程将其写入磁盘)。 2. 不要编写新文件,而是覆盖现有文件。由于所有数据块的位置事先已知,因此在编写时不会重新组织文件系统。速度也会快一点。因此,创建一个大文件或使用原始分区,然后覆盖它。当到达文件末尾时,将其定位回到开头并重复。
缺点:
使用第一种方法,您可能会丢失帧。此外,您必须确保所有客户端都能够快速读取和处理数据,否则服务器可能会被阻止。
对于第二种方法,您必须找到一种方法来告诉读者当前的“文件结尾”在哪里。
因此,也许最好采用混合方法:
1. 创建一个大文件(几GB)。如果一个文件不够,可以创建多个文件。 2. 打开套接字 3. 将数据写入文件。如果到达文件末尾,则将其定位到位置0并继续在那里编写(类似于循环缓冲区)。 4. 刷新数据 5. 通过套接字向读者发送新数据的开始和数量
考虑使用内存映射文件;这将使一切变得更简单。

关于您对使用内存映射文件的评论,几个GB大小的文件可以映射到内存中吗?我只有512MB的可用内存。此外,内存映射如何简化事情? - puffadder
是的,只要你的CPU地址空间可以处理它,文件大小就不重要。使用32位CPU,您可以管理约3.5GB的文件;使用64位CPU,文件大小无关紧要。至于简单性:http://en.wikipedia.org/wiki/Memory-mapped_file 基本上,它允许您像访问内存中的字节数组一样访问文件。 - Aaron Digulla

1

除了 RAM 和磁盘,实际上没有其他选项,只有变化。我认为这种方法是可行的:您可以获得非常好的文件系统性能。

额外的偶尔时间可能是由于文件系统正在寻找更多的空闲空间(它维护一个短列表,但当用尽时,需要进行更昂贵的搜索)并将其分配到文件中。如果这是原因,请将文件预先分配到最大大小,并使用随机 I/O (fopen(fn, "r+")) 写入文件,以便它不会截断文件长度。

另一种可能有助于稳定文件 I/O 时间的技术是在文件偏移量对齐到扇区边界的位置写入每个帧缓冲区。这样,文件系统就不必通过首先从扇区读取来保留不会被覆盖的内容来处理奇怪的偏移写操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接