Multithreading 线程、Coro、任意事件混淆

Multithreading 线程、Coro、任意事件混淆,multithreading,perl,coroutine,anyevent,Multithreading,Perl,Coroutine,Anyevent,我对perl比较陌生,甚至对perl中的线程技术也比较陌生。我有一个perl脚本,它从3个不同的源获取输入。(2个LDAP查询和一个不总是存在的文件)因为某些部分可能需要比其他部分更长的时间,所以我决定使用线程和队列。在开发过程中,测试脚本的各个组件效果很好,但将它们放在一起之后,性能似乎会下降 基本结构是这样的 2个线程:(读取文件或读取AD条目)->Queue1->2个线程:(清理数据)->Queue2->3-4个线程(与现有本地LDAP条目进行比较)。几个线程将统计信息报告回主脚本,一旦所

我对perl比较陌生,甚至对perl中的线程技术也比较陌生。我有一个perl脚本,它从3个不同的源获取输入。(2个LDAP查询和一个不总是存在的文件)因为某些部分可能需要比其他部分更长的时间,所以我决定使用线程和队列。在开发过程中,测试脚本的各个组件效果很好,但将它们放在一起之后,性能似乎会下降

基本结构是这样的 2个线程:(读取文件或读取AD条目)->Queue1->2个线程:(清理数据)->Queue2->3-4个线程(与现有本地LDAP条目进行比较)。几个线程将统计信息报告回主脚本,一旦所有线程都完成,就会发送一封包含该运行的所有统计信息和状态的电子邮件

我正在使用dequeue_nb,我认为这会有所帮助,但没有运气

对性能的冲击似乎在排队等候。在寻找提高性能的技巧时,我读了几篇文章,说perl线程不好,并且使用coro、POE、Anyevent、IO:async等


这看起来不像是一个“事件”问题,所以我不认为任何事件或POE会是我所看到的方式,coros似乎一次只使用一个CPU,所以我也不确定这是否可行。我想用它们的组合,但后来我的头开始痛了。是否有人对如何修复/排除我的脚本或如何实现其他模块有任何建议

并行性的一个问题是同步。这是一个性能杀手,它很糟糕,如果可能的话应该避免

操作系统架构 让我们看看您的体系结构:

+--------------+--------------+
|   Input 1    |   Input 2    |
+--------------+--------------+
|           QUEUE A           |
+--------------+--------------+
|   Scrub 1    |   Scrub 2    |
+--------------+--------------+
|           QUEUE B           |
+---------+---------+---------+
| Compare | Compare | Compare |
+---------+---------+---------+
讨论 队列A必须跨四个线程同步;B队穿过5-6号线。任何时候只有一个线程可以访问队列,因此大部分时间线程都在等待,无法工作

并行流水线结构 稍微不同的体系结构可能如下所示:

+-----------+    +-----------+
|  Input 1  |    |  Input 2  |
+-----------+    +-----------+
| QUEUE  1A |    | QUEUE  2A |
+-----------+    +-----------+
|  Scrub 1  |    |  Scrub 2  |
+-----------+    +-----------+
| QUEUE  1B |    | QUEUE  2B |
+-----+-----+    +-----+-----+
| Cmp | Cmp |    | Cmp | Cmp |
+-----+-----+    +-----+-----+
讨论 这里,A队列仅与两个线程相关(->更少等待),B队列仅与三个线程相关。对于类似的输入大小/复杂度,此体系结构应该执行得更快。如果输入2短得多,整个管道2将在管道1完成一半之前运行。但是,它比为每个管道使用单个进程要好得多

草坪喷灌建筑 概念 更好的体系结构将尝试将流程的输出分布在多个队列中。(反之,当队列为空时,让线程从多个队列获取其输入是错误的。)

每个队列写入都应转到不同的队列:

  +-----------+   +-----------+
  |  Input 1  |   |  Input 2  |
  +-----------+   +-----------+
        |      \ /      |
  +-----------+   +-----------+
  | QUEUE  1A |   | QUEUE  2A |
  +-----------+   +-----------+
  |  Scrub 1  |   |  Scrub 2  |
  +-----------+   +-----------+
        / | \ \   / / | \      

+-------+-------+-------+-------+
| Q. 1B | Q. 2B | Q. 3B | Q. 4B |
+-------+-------+-------+-------+
|  Cmp  |  Cmp  |  Cmp  |  Cmp  |
+-------+-------+-------+-------+
这可以确保每个线程具有相同的工作负载,但不能确保所有线程同时完成

讨论 所有队列在3个线程之间共享。问题是两个线程在写入队列时会互相阻塞。如果队列写入访问之间的时间明显大于写入持续时间,这应该没有问题,否则可以混合使用第二种体系结构

因此,这种架构是否合理取决于您的确切需求

对于大小均匀的输入,它的速度较慢,但对于不规则的输入,它的性能更好

附录 关于实施: 所使用的框架是体系结构的次要部分。如果只传递文本字符串,我强烈建议使用管道。如果必须传递Perl数据类型或对象,则可能必须接受使用真实队列的额外开销:在向队列添加非共享变量时,除了所有同步开销外,还必须进行深度复制(请参见@Leon Timmermans answer)

关于可伸缩性:
体系结构1和3在线程数量上不是固定的。我强烈建议使用这种灵活性对不同的组合进行基准测试。经验法则是,应该使用n到2n个线程,其中n是处理器(或硬件线程)的数量。这可以看作是一个阶段中线程的最大合理数量。除此之外,你只会得到一个记忆惩罚,而不会加速。性能饱和点可能会提前达到,此时阶段处理输入的速度可能比提供的速度快。

您将什么类型的数据放入队列?AFAIK简单数据比复杂结构便宜,因为它至少需要克隆和复制两次。我一直计划编写一个更快的队列实现(实际上大部分工作已经完成),但还没有发布。

对我来说,Perl中的多线程通常不值得这么麻烦;有时我使用fork()来实现并发性。实现任务的一种方法是为每个查询/输入创建一个新流程,并将结果写入文件。之后,在主进程中对数据进行munge。@amon,Forking强制使用管道,这很好。问题是,他(说他)已经在使用管道了。分叉不会改变任何事情。谢谢阿蒙的回复。我尝试过草坪洒水器的方法,但它似乎对性能没有任何帮助。从长远来看,它可能会受到一些伤害。因为这是一个对时间敏感的修复,我已经把我的产品投入生产,但只是改变了一些事情的时间安排。不过我会继续看的。由于磨砂阶段似乎非常快,而且代码相对较小,我正在考虑将其与最后一步结合起来。也许这会有帮助。第一个队列正在接收数组。第一个值是数据的源,它还告诉清理线程第二个值是什么类型的数据。第二个值是长字符串或LDAP条目的副本。一旦你有了一些(半)工作代码,我很想尝试一下。也许还不是为了制作,但也许是为了帮助你完成它。