The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Type::Tiny::Manual::Optimization - squeeze the most out of your CPU

DESCRIPTION

Various tips to help you squeeze the most out of your CPU.

XS

The simplest thing you can do to increase performance of many of Types::Standard's built-in type constraints (as well as Type::Tiny::Class and Type::Tiny::Duck) is to install Type::Tiny::XS, a set of ultra-fast type constraint checks implemented in C.

Types::Standard, Type::Tiny::Class, and Type::Tiny::Duck will attempt to load Type::Tiny::XS and use its type checks. If Type::Tiny::XS is not available, they'll then try to use Mouse if it is already loaded. (They won't attempt to load Mouse.)

Common Sense

The HashRef[ArrayRef] type constraint can probably be checked faster than HashRef[ArrayRef[Num]]. If you find yourself using very complex and slow type constraints, you should consider switching to simpler and faster ones. (Though this means you have to place a little more trust in your caller to not supply you with bad data.)

(A counter-intuitive exception to this: even though Int is more restrictive than Num, in most circumstances Int checks will run faster.)

Inlining Type Constraints

If your type constraint can be inlined, this can not only speed up Type::Tiny's own checks and coercions, it may also allow your type constraint to be inlined into generated methods such as Moose attribute accessors.

All of the constraints from Types::Standard can be inlined, as can enum, class_type, role_type and duck_type constraints. Union and intersection constraints can be inlined if their sub-constraints can be. So if you can define your own types purely in terms of these types, you automatically get inlining:

   declare HashLike, as union [
      Ref["HASH"],
      Overload["&{}"],
   ];

However, sometimes these base types are not powerful enough and you'll need to write a constraint coderef:

   declare NonEmptyHash, as HashLike,
      where     { scalar values %$_ };

... and you've suddenly sacrificed a lot of speed.

Inlining to the rescue! You can define an inlining coderef which will be passed two parameters: the constraint itself and a variable name as a string. For example, the variable name might be '$_' or '$_[0]'. Your coderef should return a Perl expression string, interpolating that variable name.

   declare NonEmptyHash, as HashLike,
      where     { scalar values %$_ },
      inline_as {
         my ($constraint, $varname) = @_;
         return sprintf(
            '%s and scalar values %%{%s}',
            $constraint->parent->inline_check($varname),
            $varname,
         );
      };

The Perl expression could be inlined within a function or a if clause or potentially anywhere, so it really must be an expression, not a statement. It should not return or exit and probably shouldn't die. (If you need loops and so on, you can output a do block.)

Note that if you're subtyping an existing type constraint, your inline_as block is also responsible for checking the parent type's constraint. This can be done quite easily, as shown in the example above.

Note that defining a type constraint in terms of a constraint coderef and an inlining coderef can be a little repetitive. Sub::Quote provides an alternative that reduces repetition (though the inlined code might not be as compact/good/fast).

   declare NonEmptyHash, as HashLike,
      constraint => quote_sub q{ scalar values %$_ };

Aside: it's been pointed out that "might not be as fast" above is a bit hand-wavy. When Type::Tiny does inlining from Sub::Quote coderefs, it needs to inline all the ancestor type constraints, and smush them together with &&. This may result in duplicate checks. For example, if 'MyArray' inherits from 'MyRef' which inherits from 'MyDef', the inlined code might end up as:

   defined($_)              # check MyDef
   && ref($_)               # check MyRef
   && ref($_) eq 'ARRAY'    # check MyArray

When just the last check would have been sufficient. A custom inline_as allows you finer control over how the type constraint is inlined.

Optimizing Coercions

Coercions are often defined using coderefs:

   PathTiny->plus_coercions(
      Str,   sub { "Path::Tiny"->new($_) },
      Undef, sub { "Path::Tiny"->new("/etc/myapp/default.conf") },
   );

But you can instead define them as strings of Perl code operating on $_:

   PathTiny->plus_coercions(
      Str,   q{ "Path::Tiny"->new($_) },
      Undef, q{ "Path::Tiny"->new("/etc/myapp/default.conf") },
   );

The latter will run faster, so is preferable at least for simple coercions.

This makes the most difference when used with Moo, which supports inlining of coercions. Moose does not inline coercions, but providing coercions as strings still allows Type::Tiny to optimize the coercion coderef it provides to Moose.

AUTHOR

Toby Inkster <tobyink@cpan.org>.

COPYRIGHT AND LICENCE

This software is copyright (c) 2013-2014 by Toby Inkster.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.