2009-05-03 128 views
4

我遇到以下问题。一堆数据分成10k个小文件(每个大约8-16kib)。根据用户输入,我必须尽可能快地加载并处理它们。更确切地说,每个数据包可以分成100-100k个文件,并且大约有1k个数据包。虽然他们大多数是较小的。从单个文件并行读取

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

所以,这个想法是将所有这些小文件合并成一个大数据包,并从中读取。我可以保证它将是只读的,但是我不知道预先并发访问一个文件的线程数(我知道最大数目)。这会给我大约1000个大小适中的文件,并且我可以轻松添加新的数据包。

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

我想过打开每个数据包N次,并给每个线程一个循环方式的句柄,但问题是,它可以以(数据文件数)x(最大数量线程)打开句柄(可以很容易地变成8-16k),并且我必须在每次访问数据包时使用同步,或者使用一些无锁魔术来获得下一个空闲文件句柄。因为这似乎不是一个原始问题(我猜,任何数据库引擎都有类似的问题,在这里你可以有N行(文件在我的情况下)的M个表(数据包),并且你想要允许尽可能多的线程同时读取行)。那么这里推荐的做法是什么?顺便说一下,它应该在Windows和Linux上运行,所以欢迎使用可移植的方法(或者至少可以在两种平台上工作的方法,即使它们使用不同的底层API--只要它们可以被包装,我很高兴)。

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

如果其中一个调用需要70 ms左右才能完成,那真的没有问题,但如果读取调用阻塞,我无法负担得起。

+0

您的数据是否存储在RAID阵列或类似的东西上? – Egwor 2009-05-03 09:58:31

+0

不,完全没有。同样,速度不是主要问题,只要它是异步运行的,即使每次读取都需要100 ms,我也可以。问题是如何让多个线程同时访问一个文件:) – Anteru 2009-05-03 10:01:46

+0

我看到映射整个文件集的内存不是一个选项,但也许你可以使用一块内存作为访问文件的大缓存大多数时候(或者它们全部只能被读取一次?) – schnaader 2009-05-03 10:02:25

回答

2

我不认为多线程将非常有助于你读取磁盘。假设文件位于一个磁盘上,您只有一组读取头可以访问它,因此您将在此处进行序列化。

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

但是你选择继续,我可以建议你确保你的代码是这样构成的,不同类型的线程数是容易配置,最好是来自可执行命令行。在这种情况下,您需要尝试不同的线程配置,以找到适合您特定情况的最佳数字。

+0

是的,但我不想每次访问文件时都会阻止。事情是,我可以在等待磁盘寻道时间的同时继续显示当前数据,并且我想尽可能减少失速。 – Anteru 2009-05-03 09:44:46

0

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

0

那将伤害你的问题是头部争;不管你运行多少个线程,头部一次只能处于一个位置。你有选择在多个磁盘上分发文件吗?

0

MMAP的方法派上用场。你并不需要为每个读做MMAP /取消映射周期,而是有一个线程处理所有这些映射,并交给指针(实际上的偏移和长度)。当线程访问映射到文件的虚拟内存时,真正的读取部分将由操作系统调度。

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

1

Linux根本没有可用的异步IO(是的,这里有aio_ *,但它只适用于O_DIRECT并具有各种奇怪的限制),所以如果你想要便携的东西,你只需要使用正常的读取调用。 mmap可以工作,但如果您每次只读取少量数据,则更改映射的成本可能会高一点。现在

,我不知道Windows,但在Linux上有一个PREAD()函数,可以让你从一个文件描述符读取在一个给定的偏移,而不会影响文件描述符的查找指针。有了这个,你可以有任意数量的线程从同一个文件中读取数据,而不需要锁定文件描述符或任何愚蠢的东西。