为啥 Moose 代码这么慢?
Posted
技术标签:
【中文标题】为啥 Moose 代码这么慢?【英文标题】:Why is Moose code so slow?为什么 Moose 代码这么慢? 【发布时间】:2011-04-24 07:47:04 【问题描述】:我正在尝试解析一个大型 XML 文件。我使用 XML::SAX 阅读它(使用 Expat,而不是 perl 实现)并将所有二级及以下节点放入我的“节点”类:
package Node;
use Moose;
has "name" =>
(
isa => "Str",
reader => 'getName'
);
has "text" =>
(
is => "rw",
isa => "Str"
);
has "attrs" =>
(
is => "rw",
isa => "HashRef[Str]"
);
has "subNodes" =>
(
is => "rw",
isa => "ArrayRef[Node]",
default => sub []
);
sub subNode
my ($self, $name) = @_;
my $subNodeRef = $self->subNodes;
my @matchingSubnodes = grep $_->getName eq $name @$subNodeRef;
if (scalar(@matchingSubnodes) == 1)
return $matchingSubnodes[0];
return undef;
1;
在“end_element”子中,我检查这是否是我关心的节点,如果是,我会做一些进一步的处理。
这一切在我的测试文件上运行良好,但前天我把它扔到我的真实文件中,全部 1300 万行,而且它需要很长时间。它已经运行了超过 36 个小时。如何判断瓶颈是 Moose 还是 XML::SAX? Moose 总是这么慢,还是我用错了?
更新对 20,000 行数据子集进行分析表明,Moose 是瓶颈 - 特别是在 Class::MOP::Class::compute_all_applicable_attributes (13.9%) 和其他 Class和驼鹿类。
【问题讨论】:
驼鹿可能看起来很慢,但不要让他生气......Class::MOP::Class::compute_all_applicable_attributes
如果您不像我在回答中建议的那样__PACKAGE__->meta->make_immutable
您的课程,则需要做很多事情。如果这样做,个人资料会如何变化?
@Ether,那个是关于启动成本的。我的是关于运行成本,尤其是对象的创建和销毁。
我很想有一个你的 XML 数据的例子来测试它,我在 XML::Toolkit 中做了类似的事情(使用捆绑的 XML::Filter::Moose)并且会很好奇分析它。
您展示的代码相当短,为什么不使用传统的 perl5 对象重写它,看看它会如何改变呢?以非常信任的方式编写它,不检查任何类型约束或使用任何防御性编程实践。这将为您提供希望摆脱 perl 对象模型的速度上限
【参考方案1】:
虽然 Moose 在启动时做了很多工作,这有时让它看起来有点慢,但它生成的代码,尤其是属性访问器之类的东西,通常比普通 perl 程序员能够快得多写。因此,鉴于您的流程的运行时间很长,我怀疑 Moose 引起的任何开销都是相关的。
但是,从您显示的代码中,我无法真正说出您的瓶颈是什么,即使我坚信它不是 Moose。我还想指出,通过__PACKAGE__->meta->make_immutable
声明您的课程现在“已完成”允许 Moose 进行一些进一步的优化,但我仍然怀疑这是给您带来麻烦的原因。
您如何从数据中抽取一个较小的样本,这样您的程序将在合理的时间内完成,并在诸如Devel::NYTProf
之类的分析器中查看它。这将能够告诉您程序中的确切时间花费在哪里,因此您可以专门优化这些部分以获得最大可能的收益。
一种可能性是您使用的类型约束会减慢速度。实际上,在对它们的每一次写访问(或在类实例化)上彻底验证实例属性并不是大多数程序员通常会做的事情。如果您对数据的有效性有足够的把握,您可以尝试使用更简单的约束,例如 ArrayRef
而不是 ArrayRef[Node]
。这样,只会检查属性值本身的类型,而不是该数组引用中每个元素的值。
但是,请分析您的代码。不要猜。
【讨论】:
NYTProf 比 DProf 好吗?__PACKAGE__->meta->make_immutable
让我的子集从 11 秒缩短到 6 秒。现在分析器说它在Moose::Meta::TypeConstraint::ArrayRef[Node]
上花费了 26% 的时间,所以我将尝试你的建议来放松对下一个限制。
放松约束没有任何明显的区别——我的 20,000 行测试文件从 6.4 秒变为 6.3 秒。
@Paul:现在的时间分解是什么样子的?它现在应该主要用于 XML 处理,而不是 Moose 内脏。
参见:ttp://***.com/questions/3162390/is-moose-really-this-slow/3162676【参考方案2】:
我已经使用XML::Twig 745mb 文件成功编写了大型 XML 处理应用程序,在一个合理大小的盒子上运行不到一个小时。
但是正如其他用户已经提到的那样,您需要分析您的代码以找出导致问题的确切原因。
【讨论】:
【参考方案3】:我高度怀疑您的速度问题不在于 Moose,而在于内存分配和磁盘交换。即使不执行 ->meta->make_immutable,根据您对 20K 子集的时间,您的脚本也应该在大约 2 小时内完成(((11 *(13_000_000 / 20_000))/60)== ~119 分钟)。通过执行 ->meta->make_immutable 它会将其减少到大约。 65分钟左右。
再次尝试运行你的大脚本,看看你的内存和交换在做什么,我怀疑你给你的磁盘带来了可怕的震动。
【讨论】:
Munin 说我在前 36 小时的跑步中几乎没有换过。从 10 月 10 日晚上到 12 月 12 日上午,请参阅 xcski.com/munin/xcski.com/allhats2.xcski.com-cpu.html、xcski.com/munin/xcski.com/allhats2.xcski.com-memory.html 和 xcski.com/munin/xcski.com/allhats2.xcski.com-swap.html。在我执行 make_immutable 之后,您可以在第二次运行期间(从 10 月 12 日上午 10 点左右开始)看到更多的交换使用,但这可能与我同时做的其他事情有关。以上是关于为啥 Moose 代码这么慢?的主要内容,如果未能解决你的问题,请参考以下文章