fseeko(FILE* stream, off_t offset, int whence) 的性能

performance of fseeko(FILE* stream, off_t offset, int whence)

我的问题:

我有一个大约 4GB 大小的文件。我从来没有使用过 fseeko/ftello,我不太熟悉文件在磁盘上的组织方式。如果我打开一个文件然后要求 fseeko 跳转到文件的第 2,348,973,408 个字节,它是否必须按顺序遍历成千上万个像链表一样链接在一起的块头(或类似的东西)到达我的 4GB 文件的中间?或者它是否有更有效的随机访问文件的方式?我正在寻找一种有效地跳转到非常大文件的特定字节的方法。如果这不能有效地工作,我考虑过将文件分成 4000 个 1 兆字节的文件,每个文件都可以更有效地 fseek'ed。有什么建议吗?

背景:

我计算了一个大型 6 维数组,其中包含近 5 亿个双精度数字,代表了一个复杂问题的解决方案。每个 8 字节的数据集,作为单个文件写入,占用将近 4 GB 的磁盘 space。我想编写一个小型服务器应用程序,它接受对该数据的小范围连续范围的请求,以及 returns 请求的数据。

现在我 可以 将整个文件读入 RAM,但我打算让这个服务器一直运行 运行,这样会占用一半的内存我的服务器上有 8GB RAM。所以我不想那样做。相反,我想将它留在磁盘上并读取请求的数据页,响应请求,然后再次从 RAM 中删除该页。

我的下一个想法是将数据加载到数据库中,但是当我存储 6 个索引以及每个 8 字节的数据值时,并在 table 上添加索引(为了快速查看ups),我认为数据库的大小将比 4GB 文件大一个数量级。那不会是世界末日,但我将来可能会添加更多这些大文件,而且我宁愿没有那么多数据。这里可能还有其他选择:我可以使用二进制 varchar 或类似的东西将整页数据存储到一行中。

但这让我想知道我是否找不到某种方法来直接从文件中有效地访问数据。我知道我想要文件的哪些字节。问题是是否有一种快速的方法可以找到它们。因此我上面关于 fseeko 的问题。

原则上fseek()要快。

然而,对于您是否在文本模式下使用文件,需要注意一点:

  • 如果在文本模式下,fseek() 保证仅对先前由 ftell() 返回的位置或文件开头的 0 位置按标准工作。对其他参数组合的支持取决于实现。幸运的是,在大多数 OS 上,它也适用于文件末尾的 0。

  • 在二进制模式下你没有这样的限制。

文本模式限制的目的是避免使用直接定位可能引起的不一致(因为文本模式在读取的字节和磁盘上的字节之间没有一对一的映射)。

编辑:关于您背景的其他信息

我假设您正在使用二进制文件以固定大小的格式存储所有这些数字:

  • 在我看来,数据库在这里没有意义。

  • 如果您的服务器运行 64 位 OS 并且您有足够的磁盘 space 用于交换区域,您可以选择将完整数据集加载到内存中:它将加载到虚拟内存中,OS 将负责优化加载到可用 RAM 中的内存页面。

  • 如果您以非常不规则的方式浏览文件,交换也可能会触发大量文件读取。然后,使用 fseek() 直接转到使用 6 个维度的索引计算的位置将是一个明智的选择。

  • 最后,您还可以选择使用memory mapped files such as the POSIX mmap() or the windows MapViewOfFile()。这非常适合数组。然而,不幸的是,这不像标准 C++ 那样可移植。

这取决于文件系统,但通常文件内的块号与磁盘上的物理块之间的映射被组织为浅的、高扇出树。由于扇出通常为 1024,因此大文件的树深度通常为二。 (ext2/3 将树深度限制为三,并且仍然设法处理巨大的文件;ext4 使用更复杂的结构,效率更高。)

(过于简单化了。这里是 more precise description

查找块号需要读取间接块(树节点),但这些往往保留在内存缓存中,因此读取相对较少。