繁体   English   中英

随机访问大型二进制文件

[英]Random access of a large binary file

我有一个大的二进制文件 (12 GB),我想从该文件中即时组装一个较小的二进制文件 (16 KB)。 假设文件在磁盘上,并且较小文件的字节在较大的二进制文件中有些随机分布。 最好和最快的方法是什么? 到目前为止,我还没有做得比三分钟更好。

我尝试过的东西或多或少具有相同的性能:

  1. 将文件转换为 HDF5 格式并使用 C 接口(慢)。
  2. 通过文件向 fseek() 写一个小 C 程序(慢)。

我怎样才能真正快速地随机访问这些数据?

我希望查询不到几秒钟。

答案基本上是“不”。

单个机械磁盘驱动器将需要 10 毫秒左右来执行寻道,因为它必须移动磁盘磁头。 16000 次寻道乘以 10 毫秒每次寻道等于 160 秒。 编写代码的方式完全没有区别; 例如 mmap() 将没有任何区别。

欢迎来到物理世界,软件人:-)。 你必须改进你的操作的地方性。

首先,对您正在访问的位置进行排序。 文件中的附近位置可能在磁盘附近,并且在附近位置之间查找比随机查找要快。

接下来,您的磁盘可能会以大约 100 兆字节/秒的速度读取顺序数据; 也就是说,它可以在执行查找所需的大约同一时间内顺序读取 1 兆字节。 因此,如果您的两个值之间的距离小于 1 兆字节,则最好读取它们之间的所有数据,而不是在它们之间执行查找。 (但是对此进行基准测试以在您的硬件上找到最佳权衡。)

最后,RAID 可以帮助提高吞吐量(但不是寻道时间)。 如果您想多线程读取代码,它还可以提供多个可以同时查找的磁盘头。

但总的来说,访问随机数据是您可以要求计算机做的最糟糕的事情,无论是在 memory 中还是在磁盘上。 而且顺序访问和随机访问之间的相对差异每年都在增加,因为物理是局部的。 (好吧,无论如何,我们在这里所依赖的物理学。)

[编辑]

@JeremyP 使用 SSD 的建议是一个很好的建议。 如果它们是一个选项,它们的有效寻道时间为 0.1 毫秒左右。 这意味着您可以期望您的代码在此类硬件上运行速度快 50-100 倍。 (我没有想到这一点,因为我通常使用 1 TB 范围内的文件,而 SSD 太贵了。)

[编辑 2]

正如@FrankH 在评论中提到的那样,我的一些建议假设文件在磁盘上是连续的,这当然不能保证。 您可以通过使用良好的文件系统(例如 XFS)并在文件创建时给出“提示”来帮助改进这一点(例如,使用posix_fallocate来通知 kernel 您打算填充一个大文件)。

好吧,您可以实现的速度在很大程度上取决于您执行的读取操作总数,以便提取构成新文件有效负载的 96 kB。

为什么呢? 因为从(旋转)磁盘的随机读取是受寻道限制的; 与重新定位磁头所需的时间相比,这样的读取速度(几乎)无限快。

由于您说访问模式是随机的,因此您也不太可能从操作系统可能决定使用的任何预读中受益; 如果您愿意,您可以通过fadvise(fd, 0, MAX_OFFSET, FADV_RANDOM);将其关闭。 在大文件的文件描述符上。 或者,如果您选择了mmap() ,则为madvise() 但这只有在您执行大量读取时才会让您受益(并且您知道大量预读将是无稽之谈)。 对于小型读取,它完全是决定总数的寻道时间。

假设您需要N次随机读取并且您有M毫秒的寻道时间,那么执行数据提取至少需要N * m毫秒(如果您有自己的磁盘......)。 没有办法打破这个障碍。

编辑:关于缓解策略的一些事情:

正如一些人所提到的,解决这个问题的关键是尽量减少搜索。 有几种策略:

  1. 如果可以,请发出异步读取(即,如果读取操作N+1不依赖于读取操作N所做的操作,那么您可以同时发出两者)。 这允许操作系统/设备驱动程序将它们排队并可能对它们重新排序(或将它们与其他并发运行的进程完成的读取合并)以实现最有效的查找。
  2. 如果您事先知道所有位置,则执行分散-收集 I/O(会想到 UN*X preadv() ),效果相同。
  3. 查询您的文件系统和/或块设备以获得最佳/最小块大小; 如何做到这一点取决于系统,请参见例如statvfs()甚至ioctl_list 如果您知道这一点,您可能会使用 Nemo 提到的技术(将“最佳”块大小内的两个小读取合并为一个大读取,无需查找)。
  4. 甚至可能使用FIEMAP / FIBMAP之类的查询接口(Windows 等价物大致是FSCTL_GET_RETRIEVAL_POINTERS )来确定文件数据的物理块在哪里,并根据它执行读取合并的决定(没有必要发出一个大的“nonseeking”如果实际上跨越物理块边界并且文件系统将其变成两个,则读取)。
  5. 如果您在相对较长的时间内建立要读取的位置,那么在您仍然计算未来读取偏移量时读取(异步)也将有助于隐藏寻道延迟,因为您正在充分利用计算周期/等待时间。

一般来说,如果以上都不适用,您将不得不硬着头皮接受寻道延迟。 如果您可以证明成本(和/或 RAM 的波动性)是合理的,请购买坚固的 state 磁盘和/或使用 RAM 支持的文件系统。

您是否尝试过映射文件? (在你的情况下,mmap64)。 这将在您访问磁盘时从磁盘中延迟读取数据。

如果您必须通过整个文件来查找您正在寻找的数据,您可以使用 SSD 加速它,但它总是会很慢。 您要查找的数据的位置是否提前知道?

该文件是文本文件还是二进制文件?

如果您必须阅读整个文件并且使用的是机械硬盘,那您就完蛋了。 假设传输速率约为1 Gigabit / seconds ,这意味着您实际上无法在不到 12 x 8 = 96 秒内通过总线获取所有位。 这假设没有寻道时间,并且处理器可以在数据进入时对其进行处理。

由于传输速率受到驱动器速度的限制,即使您确切知道要读取的每个数据字节的位置,如果它们在文件中随机分布,它仍然需要大约一样长的时间因为您必须等待磁盘旋转,直到您想要的下一个字节位于磁头下方。

如果你有一个 SSD,你可能会显着改善这一点,因为没有等待字节在头脑下出现......

我想这取决于您需要进行多少次搜索。 16000,还是更小的数字? 您可以将 12 GB 文件存储在坚固的 state 驱动器上吗? 这将减少搜索延迟。

您可以分解文件并将其存储在单独的硬盘上吗? 这将启用并行的异步搜索。

一些加快文件读取速度的提示(除了已经说过的): - 读取块大小成倍的块 - 在符合 POSIX 的系统上使用 posix_fadvise(),它向操作系统提供有关分页的建议。

使用并行或异步读取。 根据需要从多个线程、进程等发出它们,或者使用 preadv,就像 FrankH 说的那样。

这意味着您不必等待一个 I/O 请求完成后才能出现下一个请求,如果您有一个聪明的 RAID controller 和许多主轴,这将提高性能。

另一方面,如果你有一个非常愚蠢的 I/O 子系统,它可能只会产生很小的差异。 考虑使用哪个I/O 调度程序(您可以即时更改它们,无需重新启动,这真的很酷)。 轶事证据表明,如果你有“智能”硬件、cfq 或截止日期,如果你有愚蠢的硬件,“noop”是最好的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM