以下是我尝试过的一些方法,它们的表现几乎相同:
1. 将文件转换为HDF5格式并使用C接口(较慢)。 2. 编写一个小的C程序通过fseek()在文件中寻找(较慢)。
如何才能真正快速地随机访问这些数据?
我希望查询时间不到两秒钟。
@JeremyP的建议使用固态硬盘是一个好主意。如果有这个选项,它们的有效搜索时间约为0.1毫秒。这意味着您可以期望在此类硬件上运行代码的速度快50-100倍。(我没有考虑到这一点,因为我通常处理的文件大小为1TB,SSD会太昂贵。)
[编辑2]
正如@FrankH在评论中提到的那样,我的一些建议假定文件在磁盘上是连续的,这当然不能保证。您可以通过使用良好的文件系统(例如XFS)和在文件创建时提供"提示"(例如使用posix_fallocate来告知内核您打算填充大文件)来帮助改善此问题。
好的,你能够实现的速度在很大程度上取决于为提取96kB新文件的负载所执行的读取操作总数。
为什么会这样呢?因为从(旋转)磁盘进行随机读取受到查找限制;相比重定位磁头所需时间,读取本身(几乎)是无限快的。
由于你说访问模式是随机的,所以也不太可能从操作系统中获得任何预读取的好处;如果你选择这样做,可以通过对大文件的文件描述符使用 fadvise(fd,0,MAX_OFFSET,FADV_RANDOM);
将其关闭。或者,如果你选择使用mmap()
,则可以使用madvise()
。但是,这只在你执行大型读取时才有用(并且你知道大量预读取是无意义的)。对于小型读取,只有查找时间确定总体时间。
N
个随机读取,并且您有M
毫秒的寻道时间,至少需要N * m
毫秒来执行数据提取(如果您独占磁盘...)。没有办法突破这个限制。
编辑:减轻策略的一些要点:
正如一些人所提到的,解决这个问题的关键是最小化寻道。有几种策略可以采用:
N + 1
不依赖于读取操作N
的内容,则可以同时发起两个操作)。这样可以让操作系统/设备驱动程序将它们排队,并可能重新排序它们(或将它们与其他并行运行的进程完成的读取合并)以实现最有效的查找。preadv()
会出现),以同样的效果。FIEMAP
/ FIBMAP
(Windows的等价物大致是FSCTL_GET_RETRIEVAL_POINTERS
))来确定文件数据的物理块所在位置,并根据此执行读取合并的决策(如果实际上跨越了物理块边界并且文件系统将其分成两个块,则没有发出大型的“非查找”读取是没有意义的)。如果你必须读取整个文件并且使用机械硬盘,那么你就完了。假设传输速率约为 1 Gigabit/second,这意味着你物理上无法在少于12 x 8 = 96秒内将所有位通过总线传输。这假设不存在寻道时间,并且处理器可以随时处理数据。
由于传输速率受驱动器速度的限制,即使你确切知道要读取的每个字节数据的位置,如果它们随机散布在文件中,仍然需要大约相同的时间,因为你必须等待磁盘旋转,直到你想要的下一个字节位于磁头下方。
如果你有一个固态硬盘,那么你可能可以大大改善这种情况,因为不需要等待字节在磁头下出现。
我想这取决于你需要做多少次搜索。 一万六千次,还是更少? 你能把12GB的文件存储在固态硬盘上吗? 这将减少寻址延迟。
你能将文件分割并存储在不同的硬盘上吗? 这将使异步并行寻址成为可能。
使用并行或异步读取。 必要时从多个线程、进程等发出请求,或者像FrankH所说的那样使用preadv。
这意味着在下一个IO请求到来之前,您不必等待一个IO请求完成,如果您拥有聪明的RAID控制器和大量的磁盘,这将提高性能。
另一方面,如果您有一个非常愚蠢的IO子系统,它可能只会有很小的差别。 考虑使用哪个I/O调度程序(您可以随时更改它们,而无需重新启动,这真的很酷)。 据传"noop"是最好的选择,如果您有"聪明"的硬件,而cfq或deadline如果您有愚蠢的硬件。