4

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

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

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

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

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

4

2 回答 2

5

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

运维架构

让我们看看你的架构:

+--------------+--------------+
|   Input 1    |   Input 2    |
+--------------+--------------+
|           QUEUE A           |
+--------------+--------------+
|   Scrub 1    |   Scrub 2    |
+--------------+--------------+
|           QUEUE B           |
+---------+---------+---------+
| Compare | Compare | Compare |
+---------+---------+---------+

讨论

队列 A 必须跨四个线程同步;穿过 5-6 的队列 B。任何时候只有一个线程可以访问队列,所以大部分时间你的线程将等待,不工作

并行管道架构

稍微不同的架构可能如下所示:

+-----------+    +-----------+
|  Input 1  |    |  Input 2  |
+-----------+    +-----------+
| QUEUE  1A |    | QUEUE  2A |
+-----------+    +-----------+
|  Scrub 1  |    |  Scrub 2  |
+-----------+    +-----------+
| QUEUE  1B |    | QUEUE  2B |
+-----+-----+    +-----+-----+
| Cmp | Cmp |    | Cmp | Cmp |
+-----+-----+    +-----+-----+

讨论

在这里,A 队列只与两个线程相关联(-> 等待较少),B 队列只与三个线程相关联。对于类似的输入大小/复杂性,这种架构应该执行得更快。如果 Input 2 相当短,整个 Pipeline 2 将在 Pipeline 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 个线程之间共享。问题是两个线程在写入队列时会相互阻塞。如果 Queue 写入访问之间的时间明显大于写入持续时间,这应该没有问题,否则可以混入第二种架构。

因此,这种架构是否有意义取决于您的确切要求。

对于均匀大小的输入,它的速度较慢,但​​在不规则输入上表现更好。

附录

实施时:

使用什么框架是次要的架构。如果您只传递文本字符串,我强烈建议您使用管道。如果您必须传递 Perl 数据类型或对象,您可能必须接受使用真实队列的额外开销:将非共享变量添加到队列时,还必须进行深层复制(请参阅@Leon Timmermans 答案)到所有同步开销。

关于可扩展性:

架构 1 和 3 的线程数不固定。我强烈建议使用这种灵活性来对不同的组合进行基准测试。经验法则是您应该使用n2n 个线程,其中 n 是处理器(或硬件线程)的数量。这可以看作是一个阶段的线程的最大合理数。除此之外,您只会受到内存损失而没有加速。当一个阶段处理输入的速度比提供的速度快时,可能会更早地达到性能饱和点。

于 2012-07-30T20:17:31.887 回答
0

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

于 2012-07-30T21:51:06.813 回答