[英]Random access of a large binary file
我有一个大的二进制文件 (12 GB),我想从该文件中即时组装一个较小的二进制文件 (16 KB)。 假设文件在磁盘上,并且较小文件的字节在较大的二进制文件中有些随机分布。 最好和最快的方法是什么? 到目前为止,我还没有做得比三分钟更好。
我尝试过的东西或多或少具有相同的性能:
我怎样才能真正快速地随机访问这些数据?
我希望查询不到几秒钟。
答案基本上是“不”。
单个机械磁盘驱动器将需要 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
毫秒(如果您有自己的磁盘......)。 没有办法打破这个障碍。
编辑:关于缓解策略的一些事情:
正如一些人所提到的,解决这个问题的关键是尽量减少搜索。 有几种策略:
N+1
不依赖于读取操作N
所做的操作,那么您可以同时发出两者)。 这允许操作系统/设备驱动程序将它们排队并可能对它们重新排序(或将它们与其他并发运行的进程完成的读取合并)以实现最有效的查找。preadv()
),效果相同。FIEMAP
/ FIBMAP
之类的查询接口(Windows 等价物大致是FSCTL_GET_RETRIEVAL_POINTERS
)来确定文件数据的物理块在哪里,并根据它执行读取合并的决定(没有必要发出一个大的“nonseeking”如果实际上跨越物理块边界并且文件系统将其变成两个,则读取)。一般来说,如果以上都不适用,您将不得不硬着头皮接受寻道延迟。 如果您可以证明成本(和/或 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.