Shlomi,

On Jun 1, 2008, at 8:57 AM, Shlomi Fish wrote:
I said that I'd like to convert Test-Run to Moose. Well, now I'm in
the midst of the translation - many tests fail, but the problem is
that the tests now run much more slowly. It is a noticable difference,
that makes running the tests unbearable.

I have found that this is typical of sufficiently complex Class::Accessor modules, the coding style promoted by Class::Accessor seems to not match well with Moose. In particular where passing undef values into the accessors is concerned (which is almost certainly where your test failures are coming from) Moose will not allow this (a "Str" type is not "a string, but possibly undef too"). Also proper use of lazy & default/builder with Moose will help make instance generation quicker, especially in the cases where you don't always need every slot to be initialized. In those cases too, using the "predicate" option (documented in Class::MOP::Attribute) will help as well. Here is an example of this actually:

Class::Accessor version:

  {
      package Foo;
      __PACKAGE__->mk_accessors('bar');
  }

  my $foo = Foo->new; # no "bar" initialized
if ($foo->bar) { ... } # this check is cheap, because your just checking the accessor

Moose version:

  {
      package Foo;
      use Moose;

has 'bar' => (is => 'rw', lazy => 1, default => 'FOO::BAR', predicate => 'has_bar');
  }

  my $foo = Foo->new; # no "bar" initialized
if ($foo->bar) { ... } # this check is NOT cheap, because your forcing the deferred value in bar to be created if ($foo->has_bar) { ... } # this check *is* cheap, because your just checking slot existence

Now, in the above example thats not really that much of a difference, but if 'bar' were are large complex data structure or some expensively computed value, then it would make a huge difference.

It should be pointed out that the most recent Moose/Class::MOP release has a speedup which showed approx. 20-25% better performance, so things are improving. And people have seen anything from a 3x to 10x times performance increase by making their classes immutable (depending largely on the complexity of the classes I think, no one has cared enough to benchmark and figure it out).

The problem as I see it is that I'm using Plug-ins instead of Roles
and that I didn't finalise the class. Of course, I'd rather not
finalise the class, or ditch away plug-ins.

I think perhaps you misunderstand the use of the "immutable" feature. First, it is not finalizing, in the C# sense of the word, where it prohibits subclassing and such. Making a class immutable means that the metaclass can longer be altered, it allows Moose to then create an inlined constructor as well as memoize several of the more commonly needed bits of data in the metaclass. This is much less restrictive then you might think, and unless you are doing lots of - >meta hacking, you will never need to care.

I am not sure how you are doing plugins, but there are two ways to go about this in the Moose world utilizing roles.

1) Compile-time

This is plugins that you can know about during class construction time, and are *class* specific. So you simply create your class, applying the roles for the plugins. Often times, this is enough and runtime pluggable-ness is really overkill and not actually necessary. The key thing about this is that it is *class* specific, meaning you will want several instances of a specific class (with a specific set of plugins applied to it) in your application. This may require creating a few extra subclasses, but really if your application doesn't require arbitrary combinations of plugins applied to arbitrary instances of varying classes, then this may actually help increase the clarity of you design.

Now, since this is all compile-time defined, obviously it does not hamper making a class immutable.

2) Run-time

Now, sometimes your application design *does* require arbitrary combinations of plugins applied to arbitrary instances of varying classes, in this case you really do need a plugin system. In this case you can still use roles, you just apply them to an instance instead of to a class. Of course this is a more expensive operation because Moose needs to create an anon-class for you and apply the role to that, but you asked for it, and so you need to pay the cost. Moose will cache specific combinations of class+roles and try to reuse the anon-classes if possible, and it does help if you apply all the roles at once and not in succession.

Now, even though this is all done at runtime, you can still make your underlying classes immutable and get some of the performance benefits it provides.


And naturally, Moose is
also making many run-time checks that are not present in my old
Class-Accessor and are not really necessary for me. As much as I
appreciate type-checking for instance members, I'm disciplined enough
for it not to matter much.

So just remove the types, it is that simple since they are entirely optional. In fact, the accessor generated by Moose for this:

  has foo => (is => 'rw');

if *faster* the the typical Class::Accessor version since it never creates a lexical $self, and just uses $_[0] instead. Moose does as much as it can to only make you pay for the features you use, but it cannot remove the cost of features you use, but just don't really want ;)

So Moose seems impressive, but an overkill for Test-Run's needs.

I think perhaps you have not given Moose enough of a chance, and that your making this judgement before properly understanding what Moose can provide for you. Any powerful tool (like Moose), if not properly used, will seem like overkill.

So unless Moose will become faster, I won't be able to use it in
Test-Run, and will have to rely on Class-Accessor and the additional
logic I've built on top of it.

Moose is becoming faster and that is one of our primary goals right now. However in your case, by using type checks you don't want and not making your classes immutable, *you* are the one slowing your application down. The tools are right in front of you, you need only to use them.

From what I've heard of CPython, they
often re-implemented Python libraries code in C to make them faster.
Perhaps the Perl world should follow suit.

We have some C/XS in Class::MOP and the inclusion of that it provided our last major speed boost actually. There are possibly a few other places we could recode in C/XS that would provide us some more benefits, but since I am not (and never will be) a C programmer, all I can say is

... patches welcome :)


- Stevan











Reply via email to