Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only use Type::Tiny (no Mo*se) #85

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

dams
Copy link

@dams dams commented Jul 19, 2013

So, After discussion with @barefootcoder and @tobyink, I've produced this experimentation: have Method::Signature only use Type::Tiny for type checking.

It actually works very well.

With the help of @tobyink, the last bug is fixed ( when using a class name, it was currently translated to the type Object instead of InstanceOf[Foo::Bar]).

@dams dams mentioned this pull request Jul 19, 2013
@barefootcoder
Copy link
Contributor

mst requested that I add a reference here back to the original discussion (#81) as to what further work I need to do before I can address this pull request. To sum up:

  • I need to do performance testing to verify that Type::Tiny will, indeed, be faster (it almost certainly will).
  • I need to come up with some tests that verify that mingling Type::Tiny types with Moose types (and possibly others) will just DTRT.

@barefootcoder
Copy link
Contributor

Well, I finally got a chance to do the performance testing at least. Curious results:

                     Rate  Moose   Tiny  Mouse
 Moose 2.16542+-0.00022/s     -- -70.3% -77.7%
 Tiny  7.30227+-0.00072/s 237.2%     -- -24.9%
 Mouse 9.72762+-0.00097/s 349.2%  33.2%     --

(Results using Dumbbench.) Now, to be fair, this is a benchmark of functions that do nothing other than validate their parameters, which is something I specifically rail against in my own first MS talk. So I think these numbers are only useful in a relative sense, which is all I was looking for anyway, which is why I didn't bother to try to simulate anything more complex. So here's what I take out of it:

  • Type::Tiny is, in fact, slower than Mouse, unexpectedly.
  • But it's not very much slower. They're both 2-3x faster than Moose, which is the main thing we care about.
  • No one has really attempted to optimize the Type::Tiny code, whereas @schwern did in fact spend some time optimizing the Mouse/Moose code, so that difference might in fact get smaller one day.
  • And, even if it doesn't, they're close enough to each other that switching is still worth it, just for the win of getting out from under the Moose/Mouse/Moo/Mavor-of-the-Month debate.

So this is not going to keep me from moving forwards. But I thought it was interesting enough to share.

@dams
Copy link
Author

dams commented Nov 10, 2013

hi,

thank you for the benchmark. I agree with you that we an work on optimizing Type::Tiny later on if needed. Although I suspect @tobyink to already have given it a good thought.

@tobyink
Copy link

tobyink commented Nov 10, 2013

  • Type::Tiny is, in fact, slower than Mouse, unexpectedly.

Mouse's core type constraints are implemented in C.

If Mouse is loaded early enough, Type::Tiny will actually borrow its C
routines to implement most of its own core type constraints, so should be
able to achieve the same speeds as Mouse.

For custom types, Mouse is generally slower than either Type::Tiny or
Moose, as it doesn't support inlining. (Not that Method::Signatures
attempts inlining.)

  • No one has really attempted to optimize the Type::Tiny code, whereas
    @schwern did in fact spend some time optimizing the Mouse/Moose code, so
    that difference might in fact get smaller one day.

I think currently the Method::Signatures code calls $type->check($value)
each time it needs to check a value against a type constraint.

An easy win would be:

  1. When the method is first compiled, do:
    my $check = $type->compiled_check;
    (which yields a coderef); then
  2. When you need to check a value against it, do:
    $check->($value)

This saves a method lookup and an extra sub call. (And when Mouse is
loaded, the sub call saved is a Perl one rather than XS, so it makes a big
difference.) See the attached benchmarks.

-Toby

@barefootcoder
Copy link
Contributor

If Mouse is loaded early enough, Type::Tiny will actually borrow its C routines to implement most of its own core type constraints, so should be able to achieve the same speeds as Mouse.

Well, our internal signature object is a Mouse object, so Mouse is definintely going to get loaded. Is there any to insure it's loaded early enough, since apparently it wasn't in my stupid/simple benchmark? or is that dependent on the client's code?

For custom types, Mouse is generally slower than either Type::Tiny or Moose, as it doesn't support inlining.

Okay, that's a fair point: I was only testing built-in types (Str, Int, ArrayRef).

An easy win would be:
:
:

Okay, that sounds pretty cool. Perhaps I'll fiddle around with that. Are there any gotchas I need to be aware of there? any time when the compiled_check can get stale, or any situation where it might fail to return anything at all?

See the attached benchmarks.

I think GitHub ate your attachment. :-D Put it in a gist, perhaps?

@tobyink
Copy link

tobyink commented Nov 11, 2013

Well, our internal signature object is a Mouse object, so Mouse is
definintely going to get loaded. Is there any to insure it's loaded early
enough, since apparently it wasn't in my stupid/simple benchmark? or is
that dependent on the client's code?

Basically the first time a type constraint is checked, if Mouse is already
loaded, it will benefit from Mouse. If Mouse is loaded after the first
check, then it will never benefit from Mouse. But this is on a
type-by-type basis, so if you check an Int, then load Mouse, then check a
Str, all future Str checks will benefit from Mouse, but no future Int
checks will.

Okay, that sounds pretty cool. Perhaps I'll fiddle around with that. Are
there any gotchas I need to be aware of there? any time when the
compiled_check can get stale, or any situation where it might fail to
return anything at all?

The compiled check will always return a usable coderef. The $type->check
method is essentially implemented like this:

sub Type::Tiny::check {
   my $self = shift;
   $self->{compiled_check} ||= $self->_build_compiled_check;
   return $self->{compiled_check}->(@_);
}

Which is why grabbing and using the compiled_check directly is faster - it
saves what's basically just a wrapper method call.

See the attached benchmarks.

I think GitHub ate your attachment. :-D Put it in a gist, perhaps?

https://gist.github.com/tobyink/7416390

-Toby

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants