同时从单个文件中读取

时间:2009-05-03 09:37:20

标签: c windows linux concurrency io

我有以下问题情况。一堆数据被分成10k个小文件(每个大约8-16个kib)。根据用户输入,我必须尽快加载它们并处理它们。更确切地说,每个数据包可以分成100-100k个文件,并且大约有1k个数据包。但是,他们中的大多数都是较小的。

现在,我正在使用线程池并在每个文件访问时,下一个空闲线程打开文件,读取它,并返回准备显示的数据。随着未来文件数量的增长,我对这种方法并不满意,特别是如果它可能最终会出现大约100k或更多的文件(部署它肯定会很有趣;)。) p>

因此,我们的想法是将一个数据包的所有这些微小文件组合成一个大数据包,并从中读取。我可以保证它是只读的,但我不知道将在前面同时访问一个文件的线程数(我知道最大数量)。这将为我提供大约1000个大小合适的文件,我可以轻松添加新的数据包。

问题是:在这种情况下,如何允许1..N个线程从单个文件中有效读取?我可以在Windows上使用异步I / O,但对于小于64k的读取,它应该是同步的。内存映射文件不是一种选择,因为预期的大小是> 1.6 GiB,我仍然需要能够在x86上运行(除非我能有效地映射一些微小的部分,读取它,再次取消映射 - 我的内存映射经验是它与单次读取相比带来了相当大的开销)

我考虑过打开每个数据包N次,并以循环方式给每个线程一个句柄,但问题是它最终会得到(数据文件的数量)x(最大线程数) )打开句柄(可以很容易地变成8-16k),我必须在每次访问数据包时同步,或者使用一些无锁魔法来获取下一个免费文件句柄。

因为这似乎不是一个原始问题(我猜,任何数据库引擎都有一个类似的,你可以有M行(数据包)有N行(在我的情况下是文件),你想允许尽可能多的线程同时读取行)。那么这里推荐的做法是什么?顺便说一下,它应该在Windows和Linux上运行,因此欢迎便携式方法(或者至少在两个平台上都可以使用的方法,即使它们使用不同的底层API - 只要它们可以包装,我很高兴。) / p>

[ EDIT ]这不是关于速度,而是关于隐藏延迟。也就是说,我每秒读取100个这样的小文件,所以我最多只有1 mib / s。我主要担心的是寻道时间(因为我的访问模式是不可预测的),我想通过在向用户显示旧数据的同时触发读取来隐藏它们。问题是如何允许多个线程通过多个文件发出IO请求,可能有> 1个线程访问单个文件。

如果其中一个调用需要70毫秒左右完成,那真的没问题,但如果读取调用阻塞,我就负担不起。

5 个答案:

答案 0 :(得分:2)

我认为多线程不会对磁盘​​读取有很大帮助。假设文件位于一个磁盘盘片上,您只有一组读取头可以访问它,因此您可以在那里进行序列化。

在这种情况下,我想我会有一个磁盘读取过程,将文件顺序读入缓冲区(这有望最大化读取性能,因为读取头不需要移动回合太多,假设一个相当未碎片化的数据文件)以及许多读取缓冲区的处理线程,在完成处理时将它们标记为空闲。

但是,您选择继续,我是否可以建议您确保您的代码的结构使得不同类型的线程的数量可以轻松配置,最好是从可执行文件命令行。在这种情况下,您需要尝试不同的线程配置,以找到适合您特定情况的最佳数字。

答案 1 :(得分:1)

Linux根本没有可用的异步IO(是的,有aio_ *,但它只适用于O_DIRECT并且有各种奇怪的限制)所以如果你想要便携式的东西,你将只需要使用正常的读电话。 mmap会起作用,但如果你每次只读取少量数据,改变映射的成本可能会有点高。

现在,我不了解Windows,但在Linux上有一个pread()函数,它允许您从给定偏移量的文件描述符读取,而不会影响文件描述符的搜索指针。有了这个,您可以从同一个文件读取任意数量的线程,而无需锁定文件描述符或类似的任何东西。

答案 2 :(得分:0)

我能想象读取大量数据的最快方法是创建磁盘分区(主分区或逻辑分区,但没有LVM),并直接读取分区设备(例如/dev/sda5顺序,没有文件系统,每个磁盘只使用一个线程。重要的是按顺序访问原始磁盘,以避免磁盘搜索,这比顺序读取慢得多。

答案 3 :(得分:0)

会对你造成伤害的问题是争议;无论你运行多少线程,头部一次只能处于一个位置。您是否可以选择跨多个磁盘分发文件?

答案 4 :(得分:0)

mmap方法派上用场。您不需要为每次读取执行mmap / unmap循环,但是有一个线程处理所有这些映射并处理指针(实际上是偏移量和长度)。当线程访问映射到文件的虚拟内存时,操作系统将调度实际读取部分。

请记住,过多的线程不会提高阅读速度。数据库引擎通常具有非常有限的I / O线程,可满足应用程序线程的所有I / O需求。