我对perl相对较新,甚至更新于perl中的线程。我有一个perl脚本,可以从3个不同的来源获取输入。 (2个LDAP查询和一个并不总是存在的文件)因为某些部分可能需要比其他部分更长的时间,所以我决定使用线程和队列。在开发过程中,测试脚本的各个组件的效果非常好,但在将它们放在一起之后,性能似乎会降低。
基本结构是这样的 2个主题:(读取文件或读取AD条目) - >队列1 - > 2个主题:(擦洗数据) - >队列2 - > 3-4个线程(与现有的本地LDAP条目进行比较)。几个线程将统计信息报告回主脚本,一旦完成所有线程,就会发送一封电子邮件,其中包含该运行的所有统计信息和状态。
我正在使用dequeue_nb,我认为这会有所帮助,但没有运气。
性能打击似乎排在队列中。在寻找提高性能的技巧时,我遇到了几篇文章,说Perl线程不好,并且使用coro,POE,Anyevent,IO:async等。
这似乎不是一个“事件”问题所以我不认为AnyEvent或POE会从我所看到的方式过去,coros似乎一次只使用一个CPU所以我'我不确定这也会奏效。我想过使用它们的组合然后我的头开始疼了。有没有人有任何关于如何修复/解决我的脚本或任何建议如何实现其他模块的建议?
答案 0 :(得分:5)
并行性问题是同步。它是一个性能杀手,它很糟糕,如果可能的话应该避免。
让我们看看你的架构:
+--------------+--------------+
| 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答案)所有的同步开销。
架构1和3的线程数没有固定。我强烈建议使用这种灵活性来衡量不同的成分。根据经验,您应该使用 n 到 2n 线程,其中n是处理器(或硬件线程)的数量。这可以看作是一个阶段的线程的最大合理数。在此之上,你只会受到内存惩罚而且没有加速。当一个阶段可以比处理输入更快地处理输入时,可以提前达到性能饱和点。
答案 1 :(得分:0)
您在队列中放入了哪些数据? AFAIK简单数据比复杂结构便宜,因为它需要克隆并至少复制两次。我一直在计划编写一个更快的队列实现(大部分工作已经实际完成),但尚未发布。