繁体   English   中英

多核文本文件解析

[英]Multicore Text File Parsing

我有一台四核机器,想编写一些代码来解析一个利用所有四核的文本文件。 文本文件基本上每行包含一条记录。

多线程不是我的强项,所以我想知道是否有人可以给我一些我可以用来以最佳方式解析文件的模式。

我的第一个想法是将所有行读入某种队列,然后启动线程以将行从队列中拉出并处理它们,但这意味着队列必须存在于 memory 中,这些文件相当大,所以我我不那么热衷于这个想法。

我的下一个想法是有某种 controller 将在一行中读取并为其分配一个线程来解析,但我不确定 controller 是否最终会成为瓶颈,如果线程处理行的速度比它更快阅读并分配它们。

我知道可能还有比这两个更简单的解决方案,但目前我只是没有看到它。

Mark 的回答是更简单、更优雅的解决方案。 如果没有必要,为什么要构建具有线程间通信的复杂程序? 产生 4 个线程。 每个线程计算文件大小/4 以确定它的起点(和终点)。 然后每个线程可以完全独立地工作。

添加一个特殊线程来处理读取的唯一原因是,如果您希望某些行需要很长时间来处理,并且您希望这些行聚集在文件的单个部分中。 在不需要时添加线程间通信是一个非常糟糕的主意 您大大增加了引入意外瓶颈和/或同步错误的机会。

我会用你原来的想法 go 。 如果您担心队列可能会变得太大,请为它实现一个缓冲区(即如果超过 100 行则停止读取文件,如果它低于 20 行则重新开始读取。您需要进行一些测试找到最佳障碍)。 使任何线程都可能成为“读取器线程”,因为它必须锁定队列才能将项目拉出无论如何它还可以检查“低缓冲区”是否已被击中并再次开始读取。 在执行此操作时,其他线程可以读取队列的 rest。

或者,如果您愿意,让一个读取器线程将这些行分配给其他三个处理器线程(通过它们自己的队列)并实施工作窃取策略 我从来没有这样做过,所以我不知道这有多难。

这将消除单线程读取的瓶颈:

open file
for each thread n=0,1,2,3:
    seek to file offset 1/n*filesize
    scan to next complete line
    process all lines in your part of the file

我的经验是 Java,而不是 C#,如果这些解决方案不适用,我们深表歉意。

我能想到的直接解决方案是拥有一个运行 3 个线程的执行程序(例如,使用Executors .newFixedThreadPool )。 对于从输入文件中读取的每一行/记录,在执行器上启动一个作业(使用ExecutorService .submit )。 执行器将为您排队请求,并在 3 个线程之间分配。

可能存在更好的解决方案,但希望能够完成这项工作。 :-)

ETA:听起来很像 Wolfbyte 的第二个解决方案。 :-)

ETA2: System.Threading.ThreadPool在 .NET 中听起来很相似。 我从未使用过它,但它可能值得你花时间!

由于瓶颈通常在处理而不是在处理文件时读取,因此我使用生产者-消费者模式使用 go。 为了避免锁定,我会查看无锁定列表。 由于您使用的是 C#,您可以查看 Julian Bucknall 的无锁列表代码。

@lomaxx

@Derek & Mark:我希望有办法接受 2 个答案。 我最终将不得不使用 Wolfbyte 的解决方案,因为如果我将文件拆分为 n 个部分,则线程可能会遇到一批“慢”事务,但是如果我正在处理每个进程的文件保证需要等量的处理,那么我真的很喜欢您的解决方案,即只需将文件拆分为块并将每个块分配给一个线程并完成它。

不用担心。 如果集群“慢”事务是一个问题,那么排队解决方案是 go 的方法。 根据平均事务的快慢,您可能还想考虑一次将多行分配给每个工作人员。 这将减少同步开销。 同样,您可能需要优化缓冲区大小。 当然,这两个都是优化,您可能只应该在分析之后进行。 (如果它不是瓶颈,那么担心同步是没有意义的。)

如果您正在解析的文本由重复的字符串和标记组成,请将文件分成块,并且对于每个块,您可以让一个线程将其预解析为由关键字、“标点符号”、ID 字符串和值组成的标记。 字符串比较和查找可能非常昂贵,如果不必进行字符串查找和比较,将其传递给多个工作线程可以加速代码的纯逻辑/语义部分。

然后可以将预解析的数据块(您已经完成所有字符串比较并“标记化”它)传递给实际查看标记化数据的语义和排序的代码部分。

另外,您提到您担心占用大量 memory 的文件大小。 您可以采取一些措施来减少 memory 预算。

将文件拆分成块并解析它。 一次只读取与您正在处理的块一样多的块,再加上一些用于“预读”的块,因此当您在 go 到下一个块之前完成处理块时,您不会在磁盘上停顿。

或者,大文件可以被 memory 映射并“按需”加载。 如果处理文件的线程比 CPU 多(通常线程 = 1.5-2X CPU 是需求分页应用程序的一个好数字),则在 memory 映射文件的 IO 上停止的线程将自动从操作系统停止,直到它们memory 已准备就绪,其他线程将继续处理。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM