如何通过多线程提高文件读取性能?

时间:2009-09-24 07:22:27

标签: c linux unix

我需要在Linux下使用多个线程读取单个文件。 只有阅读操作,不需要写作。 文件读取不需要每次都读取整个文件。 它每次都需要读取文件的一个或多个部分。 我事先存储了每个部分的偏移量。 该文件太大而无法放入主内存。

因此,例如,许多用户想要阅读此类文件。 我使用线程或进程来读取文件以回答用户请求。 Linux下会发生什么? 所有读取操作都会排队吗? 操作系统将逐个完成文件读取? 是否有可能提高此类操作的性能?

我正在尝试实现一个用于信息检索的简单倒排索引。 我把字典放在内存中并在文件中发布列表。 每个文件都包含一段索引。 在字典中,我可以存储类似偏移的内容以指向单词发布列表的位置。 当100个用户想要在一秒钟内搜索某些内容时,他们会提交不同的查询。 因此,每个阅读将读取文件的不同部分。

6 个答案:

答案 0 :(得分:3)

尝试以最简单的方式实现它 - 让操作系统通过缓存来处理它的效率等等。看看性能如何 - 它可能根本不会成为瓶颈。操作系统通常擅长此类事情:)

假设您能够多次打开文件进行共享读取,我希望它能正常工作,而不会将所有读取操作排队。

答案 1 :(得分:2)

线程可以独立安全地读取文件,是的。最终,读操作将在操作系统级别排队,因此驱动程序将读取请求序列化到磁盘。根据访问策略(即读取缓冲区大小),读取应交错。除非你试图在一个请求中读取整个文件(你不应该这样做,因为你说它太大而不适合内存),所以读取请求将按照线程请求它们的顺序进行服务。 (我说大约是,因为磁盘驱动程序可以重新排序它在队列中知道的读取请求以优化磁盘访问)。所以你描述的应该工作得很好。操作系统会尽可能地积极地缓存读取(和预加载)。

至于改善性能,取决于所使用的数据和算法,存在许多可能性。每个线程是否真的需要读取整个文件来为每个请求提供服务?为什么一遍又一遍地读取相同的数据?你不能集中一些信息,以便线程可以共享读取的数据吗?这听起来像一个昂贵的解决方案如果你反复读取一个大于RAM的文件,那么最近很有可能被重新读取的缓存块可能会被推出缓存。也许文件的索引可以节省一些读取时间,并根据索引缓存访问?还要考虑使用mmap()将文件映射到内存中,然后当线程从不同的块读取时,操作系统将页面块进出。因此,值得重新思考数据的访问方式,以及您需要和何时访问的内容。如果您在此处发布更多信息,那么人们可能会提供更具体的建议。

请记住,最有效的操作是您不执行的操作!

答案 2 :(得分:2)

你的文件有多大并不能完全适合内存?

最好的方法是使用o / s,并使用mmap()将文件映射到(虚拟)内存,然后让线程通过内存访问文件。如果您使用的是32位计算机,则会将文件大小限制为“4GB以下,但可能超过2 GB”;如果您使用的是64位计算机,除了磁盘空间外,您并没有受到限制。

请注意,文件不一定都在mmap()的物理内存中;但是,它将全部存在于逻辑上。

答案 3 :(得分:1)

操作系统通常非常擅长优化对文件的访问(Linux以侵略性缓存而闻名。)但我认为减少读取量对于提高效率至关重要,你是否真的无法摆脱单一的共享数据结构代表一块文件?这样一个线程就会读取,而其他所有线程都会从读取中获益。由于它只是读取,因此只有在填充时才应该对数据结构进行任何争用。如果每个线程每次都读取文件的不同部分,这当然是不可行的。

鉴于您无法从缓存中获益(或多次),也无法共享文件的读取部分,因此没有太多事情要做(只读取文件),而是改进磁盘子系统:获取具有大量吞吐量的快速磁盘(RAID 10)。如果这还不够,请在不同的逻辑驱动器上创建该文件的两个或更多副本,以便能够进一步提高吞吐量。

答案 4 :(得分:0)

如果文件太大而无法放入系统内存并且您有许多需要读取整个文件的线程,那么您的应用程序很可能会受到磁盘I / O的限制......无论你如何阅读文件,无论操作系统多么聪明。

如果这是不可接受的,那么您需要为您的应用程序提供替代架构。例如,您可以将文件转换为另一种形式,允许线程在不读取整个文件的情况下获取所需的信息。或者,您可以将应用程序转换为在不同计算机上运行的单独进程,每个进程都有自己的文件副本。第三种可能性是添加一个线程,其唯一目的是读取和缓冲文件,并从缓冲区中读取现有线程。 (通过让工作线程全部工作在文件的同一区域,可以避免操作系统多次从磁盘读取部分文件。如果应用程序真的是磁盘绑定的,这可能会加快它。)< / p>

然而,所有这些都是猜想。如果没有关于应用程序及其处理文件的更多信息,很难给出正确的建议。

编辑:根据你的后续评论,似乎线程毕竟不需要访问所有文件。我的第一个建议是没有实际意义(你已经准备好了!),我的第三个建议也无济于事。我建议您按照@Jon Skeet的说法,以简单的方式实现系统。然后,如果存在性能问题,请寻找使其更快/更好的方法。例如:

  • 考虑实现最近查询及其结果的内存缓存。
  • 考虑使用多台计算机,并按关键字范围对索引文件进行分区,以便每个部分都适合一台计算机上的内存。
  • 如果您支持复杂查询,请考虑将它们拆分为简单查询并将简单查询发送到不同的计算机(例如,基于关键字分区),然后组合结果集。
  • 当用户只想查看前几个结果时,请考虑避免计算巨大结果集的方法。
几年前,我从一位同事那里借了一本关于索引的有趣教科书。我认为这是Managing Gigabytes by Witten et al

答案 5 :(得分:0)

需要注意的要点

  • 只有一个驱动程序,单个驱动器
  • 并且有来自多个线程的多个(随机)访问

在这种情况下,从多线程下面开始,事情是串行的(来自驱动程序层)......所以,你可以做的最好的事情,

  • 提高进程的优先级(如果可能),以便其他进程不会占用CPU时间
  • 在线程之间分配公平级别的计划
  • 基于访问的随机性(您可以启用或禁用缓存)
    • 例如,您可以禁用缓存,如果读取完全是随机的,并且您发现大多数时间都存在缓存错失