通过多线程处理大文件

时间:2012-07-19 10:09:13

标签: c algorithm large-files

磁盘上有一个非常大的文件(> 10G),fie中的每一行都由一个行号和一个人的名字组成,如下所示:

1 Jane
2 Perk
3 Sime
4 Perk
.. ..

我必须阅读这个大文件,找到每个名字的频率,最后按照每个名字频率的降序输出结果,如下所示:

Perk 2
Jane 1
Sime 1

正如面试官要求的那样,上述工作应该尽可能高效地完成,并且允许多线程。我的解决方案是这样的:

  1. 由于文件太大,我将文件分成几个小文件,每个小文件大约100M,通过lseek我可以找到每个小文件的开头和结尾档案(beg, end);

  2. 对于这些小文件,有一个使用人名作为键的共享哈希映射以及它显示的值多少次;

  3. 对于每个小文件,只有一个线程通过它,每次线程遇到一个人的名字时,它将在共享哈希映射中递增其对应的value;

  4. 当所有线程完成后,我认为是时候根据value字段对哈希映射进行排序。

  5. 但是因为该文件中的名称可能太多,所以排序会很慢。我没有想出如何按降序输出名称。

    希望任何人都可以帮助我解决上述问题,通过多线程和排序方式为我提供更好的解决方案。

4 个答案:

答案 0 :(得分:7)

使用map-reduce方法可能是您的问题的好主意。这种方法包括两个步骤:

  1. 地图:从文件中读取数据块并创建一个处理该数据的线程
  2. Reduce :主线程等待所有其他线程完成,然后它结合了每个线程的结果。
  3. 此解决方案的优点是您不需要在线程之间进行锁定,因为它们中的每一个都将在不同的数据块上运行。正如您所提议的那样,使用共享数据结构也可能是一种解决方案,但由于争用锁定,您可能会遇到一些开销。

    当所有线程的数据都可用时,您需要在reduce步骤中执行排序部分。但是您可能希望在映射步骤中做一些工作,以便在reduce步骤中完成整个排序更容易(更快)。

    如果您希望最后避免顺序排序,可以使用一些自定义数据结构。我会使用地图(类似于red-black treehash table)来快速查找名称。此外,我会使用heap来保持名称之间的频率顺序。当然,您需要拥有这些数据结构的并行版本。根据并行化的粗略程度,您可能会遇到锁定争用问题。

答案 1 :(得分:6)

如果我使用“有效”这个词作为面试问题,我会期待一个像“cut -f 2 -d''< file | sort | uniq -c”这样的答案,因为效率通常不是浪费时间解决已经解决的问题。实际上,这是一个好主意,我会在面试问题中添加这样的内容。

您的瓶颈将是磁盘,因此各种多线程都在过度设计解决方案(这也会违背“效率”)。如果存在旋转磁盘或者至少使缓冲区缓存更加混乱并且不太可能启用后退算法,那么拆分这样的读取会使速度变慢。不好的想法,不要这样做。

答案 2 :(得分:3)

我认为多线程不是一个好主意。程序的“慢”部分是从磁盘读取,多线程读取磁盘不会使它更快。它只会使它变得更加复杂(例如,对于每个块,您必须找到第一个“完整”行,并且您必须协调各种线程,并且每次访问时都必须锁定共享哈希映射) 。您可以使用“本地”哈希映射,然后在最后合并它们(当所有线程完成时(在10gb的末尾)合并部分哈希映射)。现在您无需同步对共享地图的访问权限。

我认为排序生成的哈希映射将是最简单的部分,如果完整的哈希映射可以保存在内存中:-)您只需将其复制到malloc(ed)内存块中{{ 1}}它的计数器。

答案 3 :(得分:3)

解决方案中的(2)和(4)步骤使其基本上是顺序的(第二步引入锁定以保持哈希映射一致,最后一步,您尝试对所有数据进行排序)

最后对哈希映射进行一步排序有点奇怪,你应该使用增量排序技术,比如heapsort(锁定所需的数据结构)或mergesort(排序部分" histogram& #34;文件,但避免合并所有内容"在最后的一个主线程中" - 尝试创建排序网络并在排序的每一步混合输出文件的内容。)

多线程读取可能是一个问题,但是对于现代SSD驱动器和积极的读取缓存,多线程并不是主要的减速因素。这一切都与同步结果排序过程有关。

以下是mergesort&parallelization的示例:http://dzmitryhuba.blogspot.com/2010/10/parallel-merge-sort.html

正如我所说,再一次,一些排序网络可能有助于实现高效的并行排序,但不是直接的"等待所有子线程 - 并且排序他们的结果"。也许,如果您有很多处理器,请进行bitonic排序。