这不是一个纯粹的编程问题,但是它会影响使用fseek()的程序的性能,因此了解它是如何工作的很重要。一点点免责声明,以免它被关闭。
我想知道在文件中间插入数据的效率如何。假设我有一个1MB数据的文件,然后我插入512KB偏移量的东西。与在文件末尾附加数据相比,效率有多高?只是为了让示例完整,我想要插入16KB的数据。
我理解答案因文件系统而异,但我认为常用文件系统中使用的技术非常相似,我只是希望得到正确的概念。
答案 0 :(得分:5)
(免责声明:我想在这个有趣的讨论中添加一些提示) 恕我直言,有一些事情需要考虑:
1)fseek不是主要的系统服务,而是库函数。要评估其性能,我们必须考虑如何实现文件流库。通常,文件I / O库在用户空间中添加了一层缓冲,因此如果目标位置在当前缓冲区内部或外部,则fseek的性能可能会大不相同。此外,I / O库使用的系统服务可能会有很大差异。即在某些系统上,如果可能,库会广泛使用文件内存映射。
2)正如您所说,不同的文件系统可能以非常不同的方式运行。特别是,我希望事务性文件系统必须做一些非常聪明的事情,并且可能很昂贵,准备好在文件中间可能回滚中止的写操作。
3)现代操作系统具有非常积极的缓存算法。缓存中可能已存在“fseeked”文件,因此操作变得更快。但是,如果其他进程产生的整体文件系统活动变得重要,它们可能会大量降级。
有任何意见吗?
答案 1 :(得分:4)
让我们假设以ext2 FS和Linux OS为例。我认为插入和追加之间不会有明显的性能差异。在这两种情况下,必须读取文件节点和偏移表,将相关的磁盘扇区映射到内存中,更新数据,稍后将数据写回磁盘。在这个示例中,如果在访问文件的某些部分时具有良好的时间和空间局部性,那将会产生很大的性能差异,因为这会减少加载/存储组合的数量。
如前所述,如果您处理的数据写入精确是FS块大小的倍数,则可以加快这两种操作,在这种情况下,您可以跳过加载阶段,只需将新块插入文件中inode数据结构。这是不切实际的,因为您需要对FS驱动程序进行低级访问,并且使用它会非常严格且不可移植。
答案 2 :(得分:3)
fseek(...)
是一个库调用,而不是OS系统调用。运行时库负责处理系统调用操作系统所涉及的实际开销,从技术上讲,fseek间接调用系统但实际上并非如此(这会带来明显的区别。库调用和系统调用之间的差异)。 fseek(...)
是一个标准的输入输出函数,无论底层系统如何......但是......这是一个很大的然而 ......
操作系统很可能将文件缓存在其内核内存中,即直接偏移到存储1和0的磁盘上的位置,它通过操作系统的内核层,超过可能是内核中最顶层的,它可以拥有文件组成的快照,即数据与其包含的内容无关(只要是指向磁盘结构的'指针',它就无关紧要了。偏移到磁盘上的lcoation是有效的!)...
当fseek(..)
发生时,会有很多空头,间接地,内核委托从磁盘读取任务,这取决于文件的碎片程度,理论上可能是“全部这个地方“,从用户 - 土地的角度来看,这可能是一个重要的开头,即C代码做fseek(...)
,它可能会散布在整个地方以收集数据进入“一个连续的数据视图”,然后插入到文件的中间,(记住在这个阶段,内核必须将位置/偏移调整到数据的实际磁盘盘片中)会被认为更慢而不是附加到文件的末尾。
原因很简单,内核“知道”最后一个偏移量是什么,只需擦除EOF标记并在后台插入更多数据,内核就必须为磁盘分配另一块内存-buffer,在完成数据附加后,在EOF标记之后调整到磁盘上位置的偏移量。
答案 3 :(得分:2)
我在Solaris上对fseek
做了一个观察,就是每次调用它都会重置FILE
的读缓冲区。然后,下一次读取将始终读取整个块(默认为8K)。因此,如果你有很多随机访问小读取,最好不使用缓冲区(setvbuf
和NULL
缓冲区),甚至使用直接系统调用(lseek
+ {{1}或者甚至更好read
只有1个系统调用而不是2个。我想这种行为在其他操作系统上是类似的。
答案 4 :(得分:1)
只有当数据大小是FS扇区的倍数但操作系统不提供此类功能时,您才能有效地将数据插入文件中间,因此您必须使用FS驱动程序的低级接口。
答案 5 :(得分:1)
在文件中间插入数据的效率低于追加到最后的数据,因为插入时必须在插入点之后移动数据,以便为插入的数据腾出空间。移动这些数据将涉及从磁盘读取它们,写入要插入的数据,然后在插入的数据之后写入旧数据。因此,插入时至少有一次额外的读写操作。